|
24 | 24 | #include "ecma-objects.h"
|
25 | 25 | #include "ecma-string-object.h"
|
26 | 26 | #include "ecma-try-catch-macro.h"
|
| 27 | +#include "fdlibm-math.h" |
27 | 28 | #include "jrt.h"
|
| 29 | +#include "jrt-libc-includes.h" |
28 | 30 |
|
29 | 31 | #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_NUMBER_BUILTIN
|
30 | 32 |
|
@@ -118,14 +120,220 @@ ecma_builtin_number_prototype_object_to_string (ecma_value_t this_arg, /**< this
|
118 | 120 | return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
|
119 | 121 | }
|
120 | 122 |
|
121 |
| - if (arguments_list_len == 0) |
| 123 | + if (arguments_list_len == 0 |
| 124 | + || ecma_number_is_nan (this_arg_number) |
| 125 | + || ecma_number_is_infinity (this_arg_number) |
| 126 | + || ecma_number_is_zero (this_arg_number)) |
122 | 127 | {
|
123 | 128 | ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number);
|
124 | 129 |
|
125 | 130 | return ecma_make_normal_completion_value (ecma_make_string_value (ret_str_p));
|
126 | 131 | }
|
| 132 | + else |
127 | 133 | {
|
128 |
| - ECMA_BUILTIN_CP_UNIMPLEMENTED (arguments_list_p); |
| 134 | + const lit_utf8_byte_t digit_chars[36] = |
| 135 | + { |
| 136 | + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| 137 | + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', |
| 138 | + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', |
| 139 | + 'u', 'v', 'w', 'x', 'y', 'z' |
| 140 | + }; |
| 141 | + |
| 142 | + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); |
| 143 | + ECMA_OP_TO_NUMBER_TRY_CATCH (arg_num, arguments_list_p[0], ret_value); |
| 144 | + |
| 145 | + uint32_t radix = ecma_number_to_uint32 (arg_num); |
| 146 | + |
| 147 | + if (radix < 2 || radix > 36) |
| 148 | + { |
| 149 | + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_RANGE)); |
| 150 | + } |
| 151 | + else if (radix == 10) |
| 152 | + { |
| 153 | + ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number); |
| 154 | + |
| 155 | + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (ret_str_p)); |
| 156 | + } |
| 157 | + else |
| 158 | + { |
| 159 | + uint64_t digits; |
| 160 | + int32_t num_digits; |
| 161 | + int32_t exponent; |
| 162 | + bool is_negative = false; |
| 163 | + |
| 164 | + if (ecma_number_is_negative (this_arg_number)) |
| 165 | + { |
| 166 | + this_arg_number = -this_arg_number; |
| 167 | + is_negative = true; |
| 168 | + } |
| 169 | + |
| 170 | + ecma_number_to_decimal (this_arg_number, &digits, &num_digits, &exponent); |
| 171 | + |
| 172 | + exponent = exponent - num_digits; |
| 173 | + bool is_scale_negative = false; |
| 174 | + |
| 175 | + /* Calculate the scale of the number in the specified radix. */ |
| 176 | + int scale = (int) -floor ((log (10) / log (radix)) * exponent); |
| 177 | + |
| 178 | + int buff_size; |
| 179 | + |
| 180 | + if (is_scale_negative) |
| 181 | + { |
| 182 | + buff_size = (int) floor ((log (this_arg_number) / log (10))) + 1; |
| 183 | + } |
| 184 | + else |
| 185 | + { |
| 186 | + buff_size = scale + ECMA_NUMBER_FRACTION_WIDTH + 2; |
| 187 | + } |
| 188 | + |
| 189 | + if (is_negative) |
| 190 | + { |
| 191 | + buff_size++; |
| 192 | + } |
| 193 | + |
| 194 | + if (scale < 0) |
| 195 | + { |
| 196 | + is_scale_negative = true; |
| 197 | + scale = -scale; |
| 198 | + } |
| 199 | + |
| 200 | + /* Normalize the number, so that it is as close to 0 exponent as possible. */ |
| 201 | + for (int i = 0; i < scale; i++) |
| 202 | + { |
| 203 | + if (is_scale_negative) |
| 204 | + { |
| 205 | + this_arg_number /= (ecma_number_t) radix; |
| 206 | + } |
| 207 | + else |
| 208 | + { |
| 209 | + this_arg_number *= (ecma_number_t) radix; |
| 210 | + } |
| 211 | + } |
| 212 | + |
| 213 | + uint64_t whole = (uint64_t) this_arg_number; |
| 214 | + ecma_number_t fraction = this_arg_number - (ecma_number_t) whole; |
| 215 | + |
| 216 | + MEM_DEFINE_LOCAL_ARRAY (buff, buff_size, lit_utf8_byte_t); |
| 217 | + int buff_index = 0; |
| 218 | + |
| 219 | + /* Calculate digits for whole part. */ |
| 220 | + while (whole > 0) |
| 221 | + { |
| 222 | + buff[buff_index++] = (lit_utf8_byte_t) (whole % radix); |
| 223 | + whole /= radix; |
| 224 | + } |
| 225 | + |
| 226 | + /* Calculate where we have to put the radix point. */ |
| 227 | + int point = is_scale_negative ? buff_index + scale : buff_index - scale; |
| 228 | + |
| 229 | + /* Reverse the digits, since they are backwards. */ |
| 230 | + for (int i = 0; i < buff_index / 2; i++) |
| 231 | + { |
| 232 | + lit_utf8_byte_t swap = buff[i]; |
| 233 | + buff[i] = buff[buff_index - i - 1]; |
| 234 | + buff[buff_index - i - 1] = swap; |
| 235 | + } |
| 236 | + |
| 237 | + bool should_round = false; |
| 238 | + /* Calculate digits for fractional part. */ |
| 239 | + for (int iter_count = 0; |
| 240 | + iter_count < ECMA_NUMBER_FRACTION_WIDTH && (fraction != 0 || is_scale_negative); |
| 241 | + iter_count++) |
| 242 | + { |
| 243 | + fraction *= (ecma_number_t) radix; |
| 244 | + lit_utf8_byte_t digit = (lit_utf8_byte_t) floor (fraction); |
| 245 | + |
| 246 | + buff[buff_index++] = digit; |
| 247 | + fraction -= (ecma_number_t) floor (fraction); |
| 248 | + |
| 249 | + if (iter_count == scale && is_scale_negative) |
| 250 | + { |
| 251 | + /* |
| 252 | + * When scale is negative, that means the original number did not have a fractional part, |
| 253 | + * but by normalizing it, we introduced one. In this case, when the iteration count reaches |
| 254 | + * the scale, we already have the number, but it may be incorrect, so we calculate |
| 255 | + * one extra digit that we round off just to make sure. |
| 256 | + */ |
| 257 | + should_round = true; |
| 258 | + break; |
| 259 | + } |
| 260 | + } |
| 261 | + |
| 262 | + if (should_round) |
| 263 | + { |
| 264 | + /* Round off last digit. */ |
| 265 | + if (buff[buff_index - 1] > radix / 2) |
| 266 | + { |
| 267 | + buff[buff_index - 2]++; |
| 268 | + } |
| 269 | + |
| 270 | + buff_index--; |
| 271 | + |
| 272 | + /* Propagate carry. */ |
| 273 | + for (int i = buff_index - 1; i > 0 && buff[i] >= radix; i--) |
| 274 | + { |
| 275 | + buff[i] = (lit_utf8_byte_t) (buff[i] - radix); |
| 276 | + buff[i - 1]++; |
| 277 | + } |
| 278 | + |
| 279 | + /* Carry propagated over the whole number, need to add a leading digit. */ |
| 280 | + if (buff[0] >= radix) |
| 281 | + { |
| 282 | + memmove (buff + 1, buff, (size_t) buff_index); |
| 283 | + buff_index++; |
| 284 | + buff[0] = 1; |
| 285 | + } |
| 286 | + } |
| 287 | + |
| 288 | + /* Remove trailing zeros from fraction. */ |
| 289 | + while (buff_index - 1 > point && buff[buff_index - 1] == 0) |
| 290 | + { |
| 291 | + buff_index--; |
| 292 | + } |
| 293 | + |
| 294 | + /* Add leading zeros in case place of radix point is negative. */ |
| 295 | + if (point <= 0) |
| 296 | + { |
| 297 | + memmove (buff - point + 1, buff, (size_t) buff_index); |
| 298 | + buff_index += -point + 1; |
| 299 | + |
| 300 | + for (int i = 0; i < -point + 1; i++) |
| 301 | + { |
| 302 | + buff[i] = 0; |
| 303 | + } |
| 304 | + |
| 305 | + point = 1; |
| 306 | + } |
| 307 | + |
| 308 | + /* Convert digits to characters. */ |
| 309 | + for (int i = 0; i < buff_index; i++) |
| 310 | + { |
| 311 | + buff[i] = digit_chars[buff[i]]; |
| 312 | + } |
| 313 | + |
| 314 | + /* Place radix point to the required position. */ |
| 315 | + if (point < buff_index) |
| 316 | + { |
| 317 | + memmove (buff + point + 1, buff + point, (size_t) buff_index); |
| 318 | + buff[point] = '.'; |
| 319 | + buff_index++; |
| 320 | + } |
| 321 | + |
| 322 | + /* Add negative sign if necessary. */ |
| 323 | + if (is_negative) |
| 324 | + { |
| 325 | + memmove (buff + 1, buff, (size_t) buff_index); |
| 326 | + buff_index++; |
| 327 | + buff[0] = '-'; |
| 328 | + } |
| 329 | + |
| 330 | + JERRY_ASSERT (buff_index <= buff_size); |
| 331 | + ecma_string_t* str_p = ecma_new_ecma_string_from_utf8 (buff, (lit_utf8_size_t) buff_index); |
| 332 | + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (str_p)); |
| 333 | + MEM_FINALIZE_LOCAL_ARRAY (buff); |
| 334 | + } |
| 335 | + ECMA_OP_TO_NUMBER_FINALIZE (arg_num); |
| 336 | + return ret_value; |
129 | 337 | }
|
130 | 338 | } /* ecma_builtin_number_prototype_object_to_string */
|
131 | 339 |
|
|
0 commit comments