diff --git a/jerry-core/ecma/base/ecma-helpers-conversion.cpp b/jerry-core/ecma/base/ecma-helpers-conversion.cpp index 05f70be801..a6be00d293 100644 --- a/jerry-core/ecma/base/ecma-helpers-conversion.cpp +++ b/jerry-core/ecma/base/ecma-helpers-conversion.cpp @@ -866,12 +866,13 @@ ecma_number_to_int32 (ecma_number_t value) /**< unsigned 32-bit integer value */ #if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 /** - * Perform conversion of binary representation of number to decimal representation with decimal exponent - */ + * Perform conversion of 128-bit binary representation of number + * to decimal representation with decimal exponent. + */ static void -ecma_number_to_zt_string_to_decimal (ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ARG (fraction_uint128), /**< mantissa */ - int32_t binary_exponent, /**< binary exponent */ - int32_t *out_decimal_exp_p) /**< out: decimal exponent */ +ecma_number_helper_binary_to_decimal (ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ARG (fraction_uint128), /**< mantissa */ + int32_t binary_exponent, /**< binary exponent */ + int32_t *out_decimal_exp_p) /**< out: decimal exponent */ { int32_t decimal_exp = 0; @@ -927,21 +928,30 @@ ecma_number_to_zt_string_to_decimal (ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ARG ( } *out_decimal_exp_p = decimal_exp; -} /* ecma_number_to_zt_string_to_decimal */ +} /* ecma_number_helper_binary_to_decimal */ + +#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 */ /** - * Calculate s, n and k parameters for specified ecma-number according to ECMA-262 v5, 9.8.1, item 5 - */ -static void -ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number */ - uint64_t *out_digits_p, /**< out: digits */ - int32_t *out_digits_num_p, /**< out: number of digits */ - int32_t *out_decimal_exp_p) /**< out: decimal exponent */ + * Perform conversion of ecma-number to decimal representation with decimal exponent + * + * Note: + * The calculated values correspond to s, n, k parameters in ECMA-262 v5, 9.8.1, item 5: + * - s represents digits of the number; + * - k is the number of digits; + * - n is the decimal exponent. + */ +void +ecma_number_to_decimal (ecma_number_t num, /**< ecma-number */ + uint64_t *out_digits_p, /**< out: digits */ + int32_t *out_digits_num_p, /**< out: number of digits */ + int32_t *out_decimal_exp_p) /**< out: decimal exponent */ { JERRY_ASSERT (!ecma_number_is_nan (num)); JERRY_ASSERT (!ecma_number_is_zero (num)); JERRY_ASSERT (!ecma_number_is_infinity (num)); +#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 ecma_number_t num_m1 = ecma_number_get_prev (num); ecma_number_t num_p1 = ecma_number_get_next (num); @@ -978,9 +988,9 @@ ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number (fraction_uint64_p1) >> 32u, ((fraction_uint64_p1) << 32u) >> 32u); - ecma_number_to_zt_string_to_decimal (fraction_uint128, binary_exponent, &decimal_exp); - ecma_number_to_zt_string_to_decimal (fraction_uint128_m1, binary_exponent_m1, &decimal_exp_m1); - ecma_number_to_zt_string_to_decimal (fraction_uint128_p1, binary_exponent_p1, &decimal_exp_p1); + ecma_number_helper_binary_to_decimal (fraction_uint128, binary_exponent, &decimal_exp); + ecma_number_helper_binary_to_decimal (fraction_uint128_m1, binary_exponent_m1, &decimal_exp_m1); + ecma_number_helper_binary_to_decimal (fraction_uint128_p1, binary_exponent_p1, &decimal_exp_p1); if (ECMA_NUMBER_CONVERSION_128BIT_INTEGER_IS_ZERO (fraction_uint128_m1)) { @@ -1018,7 +1028,6 @@ ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number /* While fraction doesn't fit to integer, divide it by 10 and simultaneously increment decimal exponent */ -#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 uint64_t digits_min, digits_max; while (!ECMA_NUMBER_CONVERSION_128BIT_INTEGER_IS_HIGH_BIT_MASK_ZERO (fraction_uint128_m1, 63)) @@ -1036,27 +1045,6 @@ ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ROUND_MIDDLE_AND_LOW_TO_UINT64 (fraction_uint128_p1, digits_max); digits_min++; -#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 - uint64_t digits_uint64; - uint32_t digits_min, digits_max; - - while (!ECMA_NUMBER_CONVERSION_128BIT_INTEGER_IS_HIGH_BIT_MASK_ZERO (fraction_uint128_m1, 31)) - { - ECMA_NUMBER_CONVERSION_128BIT_INTEGER_DIV_10 (fraction_uint128_m1); - decimal_exp_m1++; - } - while (!ECMA_NUMBER_CONVERSION_128BIT_INTEGER_IS_HIGH_BIT_MASK_ZERO (fraction_uint128_p1, 31)) - { - ECMA_NUMBER_CONVERSION_128BIT_INTEGER_DIV_10 (fraction_uint128_p1); - decimal_exp_p1++; - } - - ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ROUND_MIDDLE_AND_LOW_TO_UINT64 (fraction_uint128_m1, digits_uint64); - digits_min = (uint32_t) digits_uint64 + 1; - - ECMA_NUMBER_CONVERSION_128BIT_INTEGER_ROUND_MIDDLE_AND_LOW_TO_UINT64 (fraction_uint128_p1, digits_uint64); - digits_max = (uint32_t) digits_uint64; -#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 */ if (decimal_exp_m1 < decimal_exp_p1) { @@ -1085,11 +1073,8 @@ ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number uint64_t digits = (digits_min + digits_max + 1) / 2; int32_t digits_num = 0; -#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 uint64_t t = digits; -#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 - uint32_t t = (uint32_t) digits; -#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 */ + while (t != 0) { t /= 10; @@ -1101,9 +1086,128 @@ ecma_number_to_zt_string_calc_number_params (ecma_number_t num, /**< ecma-number *out_digits_p = digits; *out_digits_num_p = digits_num; *out_decimal_exp_p = decimal_exp_p1 + digits_num; -} /* ecma_number_to_zt_string_calc_number_params */ -#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 */ +#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 + /* Less precise conversion */ + uint64_t fraction_uint64; + uint32_t fraction; + int32_t exponent; + int32_t dot_shift; + int32_t decimal_exp = 0; + + dot_shift = ecma_number_get_fraction_and_exponent (num, &fraction_uint64, &exponent); + + fraction = (uint32_t) fraction_uint64; + JERRY_ASSERT (fraction == fraction_uint64); + + if (exponent != 0) + { + ecma_number_t t = 1.0f; + bool do_divide; + + if (exponent < 0) + { + do_divide = true; + + while (exponent <= 0) + { + t *= 2.0f; + exponent++; + + if (t >= 10.0f) + { + t /= 10.0f; + decimal_exp--; + } + + JERRY_ASSERT (t < 10.0f); + } + + while (t > 1.0f) + { + exponent--; + t /= 2.0f; + } + } + else + { + do_divide = false; + + while (exponent >= 0) + { + t *= 2.0f; + exponent--; + + if (t >= 10.0f) + { + t /= 10.0f; + decimal_exp++; + } + + JERRY_ASSERT (t < 10.0f); + } + + while (t > 2.0f) + { + exponent++; + t /= 2.0f; + } + } + + if (do_divide) + { + fraction = (uint32_t) ((ecma_number_t) fraction / t); + } + else + { + fraction = (uint32_t) ((ecma_number_t) fraction * t); + } + } + + uint32_t s; + int32_t n; + int32_t k; + + if (exponent > 0) + { + fraction <<= exponent; + } + else + { + fraction >>= -exponent; + } + + const int32_t int_part_shift = dot_shift; + const uint32_t frac_part_mask = ((((uint32_t)1) << int_part_shift) - 1); + + uint32_t int_part = fraction >> int_part_shift; + uint32_t frac_part = fraction & frac_part_mask; + + s = int_part; + k = 1; + n = decimal_exp + 1; + + JERRY_ASSERT (int_part < 10); + + while (k < ECMA_NUMBER_MAX_DIGITS + && frac_part != 0) + { + frac_part *= 10; + + uint32_t new_frac_part = frac_part & frac_part_mask; + uint32_t digit = (frac_part - new_frac_part) >> int_part_shift; + s = s * 10 + digit; + k++; + frac_part = new_frac_part; + } + + JERRY_ASSERT (k > 0); + + *out_digits_p = s; + *out_digits_num_p = k; + *out_decimal_exp_p = n; +#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 */ +} /* ecma_number_to_decimal */ /** * Convert ecma-number to zero-terminated string @@ -1172,147 +1276,14 @@ ecma_number_to_zt_string (ecma_number_t num, /**< ecma-number */ } else { -#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 - uint64_t fraction_uint64; - int32_t binary_exponent; - - ecma_number_get_fraction_and_exponent (num, &fraction_uint64, &binary_exponent); - /* mantissa */ - uint64_t s_uint64; + uint64_t s; /* decimal exponent */ int32_t n; /* number of digits in k */ int32_t k; - ecma_number_to_zt_string_calc_number_params (num, - &s_uint64, - &k, - &n); - -#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 - uint64_t s = s_uint64; -#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 - uint32_t s = (uint32_t) s_uint64; - - JERRY_ASSERT (s == s_uint64); -#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 */ - -#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 - /* Less precise conversion */ - - uint64_t fraction_uint64; - uint32_t fraction; - int32_t exponent; - int32_t dot_shift; - int32_t decimal_exp = 0; - - dot_shift = ecma_number_get_fraction_and_exponent (num, &fraction_uint64, &exponent); - - fraction = (uint32_t) fraction_uint64; - JERRY_ASSERT (fraction == fraction_uint64); - - if (exponent != 0) - { - ecma_number_t t = 1.0f; - bool do_divide; - - if (exponent < 0) - { - do_divide = true; - - while (exponent <= 0) - { - t *= 2.0f; - exponent++; - - if (t >= 10.0f) - { - t /= 10.0f; - decimal_exp--; - } - - JERRY_ASSERT (t < 10.0f); - } - - while (t > 1.0f) - { - exponent--; - t /= 2.0f; - } - } - else - { - do_divide = false; - - while (exponent >= 0) - { - t *= 2.0f; - exponent--; - - if (t >= 10.0f) - { - t /= 10.0f; - decimal_exp++; - } - - JERRY_ASSERT (t < 10.0f); - } - - while (t > 2.0f) - { - exponent++; - t /= 2.0f; - } - } - - if (do_divide) - { - fraction = (uint32_t) ((ecma_number_t) fraction / t); - } - else - { - fraction = (uint32_t) ((ecma_number_t) fraction * t); - } - } - - uint32_t s; - int32_t n; - int32_t k; - - if (exponent > 0) - { - fraction <<= exponent; - } - else - { - fraction >>= -exponent; - } - - const int32_t int_part_shift = dot_shift; - const uint32_t frac_part_mask = ((((uint32_t)1) << int_part_shift) - 1); - - uint32_t int_part = fraction >> int_part_shift; - uint32_t frac_part = fraction & frac_part_mask; - - s = int_part; - k = 1; - n = decimal_exp + 1; - - JERRY_ASSERT (int_part < 10); - - while (k < ECMA_NUMBER_MAX_DIGITS - && frac_part != 0) - { - frac_part *= 10; - - uint32_t new_frac_part = frac_part & frac_part_mask; - uint32_t digit = (frac_part - new_frac_part) >> int_part_shift; - s = s * 10 + digit; - k++; - frac_part = new_frac_part; - } -#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 */ + ecma_number_to_decimal (num, &s, &k, &n); // 6. if (k <= n && n <= 21) diff --git a/jerry-core/ecma/base/ecma-helpers-number.cpp b/jerry-core/ecma/base/ecma-helpers-number.cpp index dd27521ee2..b99a639a4d 100644 --- a/jerry-core/ecma/base/ecma-helpers-number.cpp +++ b/jerry-core/ecma/base/ecma-helpers-number.cpp @@ -1,4 +1,5 @@ /* Copyright 2014 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index b03f98c78d..ae7bd4edab 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -1,4 +1,5 @@ /* Copyright 2014-2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -182,6 +183,10 @@ extern ecma_number_t ecma_number_sqrt (ecma_number_t num); extern ecma_number_t ecma_number_abs (ecma_number_t num); extern ecma_number_t ecma_number_ln (ecma_number_t num); extern ecma_number_t ecma_number_exp (ecma_number_t num); +extern void ecma_number_to_decimal (ecma_number_t num, + uint64_t *out_digits_p, + int32_t *out_digits_num_p, + int32_t *out_decimal_exp_p); /* ecma-helpers-values-collection.c */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp index 4fa7526812..38c44febc1 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-number-prototype.cpp @@ -1,4 +1,5 @@ /* Copyright 2014-2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +45,34 @@ * @{ */ +/** + * Helper for rounding numbers + * + * @return rounded number + */ +static uint64_t +ecma_builtin_number_prototype_helper_round (uint64_t digits, /**< actual number **/ + int32_t round_num) /**< number of digits to round off **/ +{ + int8_t digit = 0; + + /* Remove unneeded precision digits. */ + while (round_num > 0) + { + digit = (int8_t) (digits % 10); + digits /= 10; + round_num--; + } + + /* Round the last digit up if neccessary */ + if (digit >= 5) + { + digits++; + } + + return digits; +} /* ecma_builtin_number_prototype_helper_round */ + /** * The Number.prototype object's 'toString' routine * @@ -170,7 +199,185 @@ static ecma_completion_value_t ecma_builtin_number_prototype_object_to_fixed (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + ECMA_OP_TO_NUMBER_TRY_CATCH (this_num, this_arg, ret_value); + ECMA_OP_TO_NUMBER_TRY_CATCH (arg_num, arg, ret_value); + + /* 1. */ + int32_t frac_digits = ecma_number_to_int32 (arg_num); + + /* 2. */ + if (frac_digits < 0 || frac_digits > 20) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_RANGE)); + } + else + { + /* 4. */ + if (ecma_number_is_nan (this_num)) + { + ecma_string_t *nan_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_NAN); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (nan_str_p)); + } + else + { + bool is_negative = false; + + /* 6. */ + if (ecma_number_is_negative (this_num)) + { + is_negative = true; + this_num *= -1; + } + + /* We handle infinities separately. */ + if (ecma_number_is_infinity (this_num)) + { + ecma_string_t *infinity_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_INFINITY_UL); + + if (is_negative) + { + ecma_string_t *neg_str_p = ecma_new_ecma_string ((const ecma_char_t *) "-"); + ecma_string_t *neg_inf_str_p = ecma_concat_ecma_strings (neg_str_p, infinity_str_p); + ecma_deref_ecma_string (infinity_str_p); + ecma_deref_ecma_string (neg_str_p); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (neg_inf_str_p)); + } + else + { + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (infinity_str_p)); + } + } + else + { + uint64_t digits = 0; + int32_t num_digits = 0; + int32_t exponent = 1; + + /* Get the parameters of the number if non-zero. */ + if (!ecma_number_is_zero (this_num)) + { + ecma_number_to_decimal (this_num, &digits, &num_digits, &exponent); + } + + digits = ecma_builtin_number_prototype_helper_round (digits, num_digits - exponent - frac_digits); + + /* 7. */ + if (exponent > 21) + { + ret_value = ecma_builtin_number_prototype_object_to_string (this_arg, NULL, 0); + } + /* 8. */ + else + { + /* Buffer that is used to construct the string. */ + int buffer_size = (exponent > 0) ? exponent + frac_digits + 1 : frac_digits + 2; + JERRY_ASSERT (buffer_size > 0); + MEM_DEFINE_LOCAL_ARRAY (buff, buffer_size, ecma_char_t); + + ecma_char_t* p = buff; + + if (is_negative) + { + *p++ = '-'; + } + + int8_t digit = 0; + uint64_t s = 1; + + /* Calculate the magnitude of the number. This is used to get the digits from left to right. */ + while (s <= digits) + { + s *= 10; + } + + if (exponent <= 0) + { + /* Add leading zeros. */ + *p++ = '0'; + + if (frac_digits != 0) + { + *p++ = '.'; + } + + for (int i = 0; i < -exponent && i < frac_digits; i++) + { + *p++ = '0'; + } + + /* Add significant digits. */ + for (int i = -exponent; i < frac_digits; i++) + { + digit = 0; + s /= 10; + + while (digits >= s && s > 0) + { + digits -= s; + digit++; + } + + *p = (ecma_char_t) ((ecma_char_t) digit + '0'); + p++; + } + } + else + { + /* Add significant digits. */ + for (int i = 0; i < exponent; i++) + { + digit = 0; + s /= 10; + + while (digits >= s && s > 0) + { + digits -= s; + digit++; + } + + *p = (ecma_char_t) ((ecma_char_t) digit + '0'); + p++; + } + + /* Add the decimal point after whole part. */ + if (frac_digits != 0) + { + *p++ = '.'; + } + + /* Add neccessary fracion digits. */ + for (int i = 0; i < frac_digits; i++) + { + digit = 0; + s /= 10; + + while (digits >= s && s > 0) + { + digits -= s; + digit++; + } + + *p = (ecma_char_t) ((ecma_char_t) digit + '0'); + p++; + } + } + + /* String terminator. */ + *p = 0; + ecma_string_t* str = ecma_new_ecma_string ((ecma_char_t *) buff); + + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (str)); + MEM_FINALIZE_LOCAL_ARRAY (buff); + } + } + } + } + + ECMA_OP_TO_NUMBER_FINALIZE (arg_num); + ECMA_OP_TO_NUMBER_FINALIZE (this_num); + return ret_value; } /* ecma_builtin_number_prototype_object_to_fixed */ /** diff --git a/tests/jerry/number_prototype_to_fixed.js b/tests/jerry/number_prototype_to_fixed.js new file mode 100644 index 0000000000..82e0bf07cb --- /dev/null +++ b/tests/jerry/number_prototype_to_fixed.js @@ -0,0 +1,61 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// Copyright 2015 University of Szeged. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//This test will not pass on FLOAT32 due to precision issues + +assert((123.56).toFixed() === "124"); +assert((123.56).toFixed(0) === "124"); +assert((123.56).toFixed(1) === "123.6"); +assert((123.56).toFixed(5) === "123.56000"); +assert((1.23e-10).toFixed(2) === "0.00"); +assert((1.23e+20).toFixed(2) === "123000000000000000000.00"); +assert((1.23e+21).toFixed(2) === "1.23e+21"); +assert((-1.23).toFixed(1) === "-1.2"); +assert((0.00023).toFixed(0) === "0"); +assert((0.356).toFixed(2) === "0.36"); +assert((0.0000356).toFixed(5) === "0.00004"); +assert((0.000030056).toFixed(7) === "0.0000301"); +assert(Infinity.toFixed(0) === "Infinity"); +assert((-Infinity).toFixed(0) === "-Infinity"); +assert(NaN.toFixed(0) === "NaN"); +assert((0.0).toFixed(0) === "0"); +assert((0.0).toFixed(1) === "0.0"); +assert((-0.0).toFixed(0) === "-0"); +assert((-0.0).toFixed(1) === "-0.0"); +assert((123456789012345678901.0).toFixed(20) === "123456789012345680000.00000000000000000000"); + +var obj = { toFixed : Number.prototype.toFixed }; +assert(obj.toFixed(0) === "NaN"); + +try { + assert(obj.toFixed(-1)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + (12).toFixed(-1); + assert(false); +} catch (e) { + assert(e instanceof RangeError) +} + +try { + (12).toFixed(21); + assert(false); +} catch (e) { + assert(e instanceof RangeError) +}