From 1efaefaabe0e42187c2f1c0d3a107abd44b4cdb8 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Mon, 22 Jun 2015 15:35:08 +0300 Subject: [PATCH 1/2] Extract part of ecma_op_number_remainder (ECMA-262 v5, 11.5.3), corresponding to item 6 (actual remainder calculation, without handling of NaN. Infinity and zero values), as ecma_number_calc_remainder. JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- jerry-core/ecma/base/ecma-helpers-number.cpp | 27 +++++++++++++++++++ jerry-core/ecma/base/ecma-helpers.h | 1 + .../operations/ecma-number-arithmetic.cpp | 13 +-------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/jerry-core/ecma/base/ecma-helpers-number.cpp b/jerry-core/ecma/base/ecma-helpers-number.cpp index b99a639a4d..6a67a17fa0 100644 --- a/jerry-core/ecma/base/ecma-helpers-number.cpp +++ b/jerry-core/ecma/base/ecma-helpers-number.cpp @@ -707,6 +707,33 @@ ecma_number_trunc (ecma_number_t num) /**< ecma-number */ } } /* ecma_number_trunc */ +/** + * Calculate remainder of division of two numbers, + * as specified in ECMA-262 v5, 11.5.3, item 6. + * + * Note: + * operands shouldn't contain NaN, Infinity, or zero. + * + * @return number - calculated remainder. + */ +ecma_number_t +ecma_number_calc_remainder (ecma_number_t left_num, /**< left operand */ + ecma_number_t right_num) /**< right operand */ +{ + ecma_number_t n = left_num, d = right_num; + + JERRY_ASSERT (!ecma_number_is_nan (n) + && !ecma_number_is_zero (n) + && !ecma_number_is_infinity (n)); + JERRY_ASSERT (!ecma_number_is_nan (d) + && !ecma_number_is_zero (d) + && !ecma_number_is_infinity (d)); + + ecma_number_t q = ecma_number_trunc (ecma_number_divide (n, d)); + + return ecma_number_substract (n, ecma_number_multiply (d, q)); +} /* ecma_number_calc_remainder */ + /** * ECMA-number addition. * diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 25c289e7de..987e6345f6 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -185,6 +185,7 @@ extern ecma_number_t ecma_number_get_prev (ecma_number_t num); extern ecma_number_t ecma_number_get_next (ecma_number_t num); extern ecma_number_t ecma_number_negate (ecma_number_t num); extern ecma_number_t ecma_number_trunc (ecma_number_t num); +extern ecma_number_t ecma_number_calc_remainder (ecma_number_t left_num, ecma_number_t right_num); extern ecma_number_t ecma_number_add (ecma_number_t left_num, ecma_number_t right_num); extern ecma_number_t ecma_number_substract (ecma_number_t left_num, ecma_number_t right_num); extern ecma_number_t ecma_number_multiply (ecma_number_t left_num, ecma_number_t right_num); diff --git a/jerry-core/ecma/operations/ecma-number-arithmetic.cpp b/jerry-core/ecma/operations/ecma-number-arithmetic.cpp index 4e6681d1f0..5e517cbafa 100644 --- a/jerry-core/ecma/operations/ecma-number-arithmetic.cpp +++ b/jerry-core/ecma/operations/ecma-number-arithmetic.cpp @@ -38,8 +38,6 @@ ecma_number_t ecma_op_number_remainder (ecma_number_t left_num, /**< left operand */ ecma_number_t right_num) /**< right operand */ { - TODO (Check precision); - ecma_number_t n = left_num, d = right_num; if (ecma_number_is_nan (n) @@ -56,16 +54,7 @@ ecma_op_number_remainder (ecma_number_t left_num, /**< left operand */ return n; } - JERRY_ASSERT (!ecma_number_is_nan (n) - && !ecma_number_is_zero (n) - && !ecma_number_is_infinity (n)); - JERRY_ASSERT (!ecma_number_is_nan (d) - && !ecma_number_is_zero (d) - && !ecma_number_is_infinity (d)); - - ecma_number_t q = ecma_number_trunc (ecma_number_divide (n, d)); - - return ecma_number_substract (n, ecma_number_multiply (d, q)); + return ecma_number_calc_remainder (n, d); } /* ecma_op_number_remainder */ /** From 913519dd5c1a4c5801b2e59dd399b4a27442823c Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Mon, 22 Jun 2015 16:40:43 +0300 Subject: [PATCH 2/2] Fix of ToUInt32 (ecma_number_to_uint32) and ToInt32 (ecma_number_to_int32) conversion routines. Related issue: #160 JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- .../ecma/base/ecma-helpers-conversion.cpp | 111 +++++++++++++++--- tests/unit/test-number-to-integer.cpp | 109 +++++++++++++++++ 2 files changed, 206 insertions(+), 14 deletions(-) create mode 100644 tests/unit/test-number-to-integer.cpp diff --git a/jerry-core/ecma/base/ecma-helpers-conversion.cpp b/jerry-core/ecma/base/ecma-helpers-conversion.cpp index a6be00d293..b793796fc0 100644 --- a/jerry-core/ecma/base/ecma-helpers-conversion.cpp +++ b/jerry-core/ecma/base/ecma-helpers-conversion.cpp @@ -823,24 +823,80 @@ ecma_int32_to_number (int32_t value) /**< signed 32-bit integer value */ } /* ecma_int32_to_number */ /** - * ECMA-defined conversion of Number value to Uint32 value + * ECMA-defined conversion of Number value to UInt32 value * * See also: * ECMA-262 v5, 9.6 * - * @return number - result of conversion. + * @return 32-bit unsigned integer - result of conversion. */ uint32_t -ecma_number_to_uint32 (ecma_number_t value) /**< unsigned 32-bit integer value */ +ecma_number_to_uint32 (ecma_number_t num) /**< ecma-number */ { - if (ecma_number_is_nan (value) - || ecma_number_is_zero (value) - || ecma_number_is_infinity (value)) + if (ecma_number_is_nan (num) + || ecma_number_is_zero (num) + || ecma_number_is_infinity (num)) { return 0; } - return (uint32_t) value; + bool sign = ecma_number_is_negative (num); + ecma_number_t abs_num; + + if (sign) + { + abs_num = ecma_number_negate (num); + } + else + { + abs_num = num; + } + + // 2 ^ 32 + const uint64_t uint64_2_pow_32 = (1ull << 32); + + const ecma_number_t num_2_pow_32 = (float) uint64_2_pow_32; + + ecma_number_t num_in_uint32_range; + + if (abs_num >= num_2_pow_32) + { + num_in_uint32_range = ecma_number_calc_remainder (abs_num, + num_2_pow_32); + } + else + { + num_in_uint32_range = abs_num; + } + + // Check that the floating point value can be represented with uint32_t + JERRY_ASSERT (num_in_uint32_range < uint64_2_pow_32); + uint32_t uint32_num = (uint32_t) num_in_uint32_range; + + uint32_t ret; + + if (sign) + { + ret = -uint32_num; + } + else + { + ret = uint32_num; + } + +#ifndef JERRY_NDEBUG + if (sign + && uint32_num != 0) + { + JERRY_ASSERT (ret == uint64_2_pow_32 - uint32_num); + } + else + { + JERRY_ASSERT (ret == uint32_num); + } +#endif /* !JERRY_NDEBUG */ + + return ret; } /* ecma_number_to_uint32 */ /** @@ -849,19 +905,46 @@ ecma_number_to_uint32 (ecma_number_t value) /**< unsigned 32-bit integer value * * See also: * ECMA-262 v5, 9.5 * - * @return number - result of conversion. + * @return 32-bit signed integer - result of conversion. */ int32_t -ecma_number_to_int32 (ecma_number_t value) /**< unsigned 32-bit integer value */ +ecma_number_to_int32 (ecma_number_t num) /**< ecma-number */ { - if (ecma_number_is_nan (value) - || ecma_number_is_zero (value) - || ecma_number_is_infinity (value)) + uint32_t uint32_num = ecma_number_to_uint32 (num); + + // 2 ^ 32 + const int64_t int64_2_pow_32 = (1ll << 32); + + // 2 ^ 31 + const uint32_t uint32_2_pow_31 = (1ull << 31); + + int32_t ret; + + if (uint32_num >= uint32_2_pow_31) { - return 0; + ret = (int32_t) (uint32_num - int64_2_pow_32); + } + else + { + ret = (int32_t) uint32_num; + } + +#ifndef JERRY_NDEBUG + int64_t int64_num = uint32_num; + + JERRY_ASSERT (int64_num >= 0); + + if (int64_num >= uint32_2_pow_31) + { + JERRY_ASSERT (ret == int64_num - int64_2_pow_32); + } + else + { + JERRY_ASSERT (ret == int64_num); } +#endif /* !JERRY_NDEBUG */ - return (int32_t) (uint32_t) value; + return ret; } /* ecma_number_to_int32 */ #if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 diff --git a/tests/unit/test-number-to-integer.cpp b/tests/unit/test-number-to-integer.cpp new file mode 100644 index 0000000000..fbd42f9cac --- /dev/null +++ b/tests/unit/test-number-to-integer.cpp @@ -0,0 +1,109 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include "ecma-globals.h" +#include "ecma-helpers.h" + +#include "test-common.h" + +typedef struct +{ + ecma_number_t num; + uint32_t uint32_num; +} uint32_test_case_t; + +typedef struct +{ + ecma_number_t num; + int32_t int32_num; +} int32_test_case_t; + +/** + * Unit test's main function. + */ +int +main (int __attr_unused___ argc, + char __attr_unused___ **argv) +{ + TEST_INIT (); + + const uint32_test_case_t test_cases_uint32[] = + { +#define TEST_CASE(num, uint32) { num, uint32 } + TEST_CASE (1.0, 1), + TEST_CASE (0.0, 0), + TEST_CASE (ecma_number_negate (0.0), 0), + TEST_CASE (NAN, 0), + TEST_CASE (-NAN, 0), + TEST_CASE (INFINITY, 0), + TEST_CASE (-INFINITY, 0), + TEST_CASE (0.1, 0), + TEST_CASE (-0.1, 0), + TEST_CASE (1.1, 1), + TEST_CASE (-1.1, 4294967295), + TEST_CASE (4294967295, 4294967295), + TEST_CASE (-4294967295, 1), + TEST_CASE (4294967296, 0), + TEST_CASE (-4294967296, 0), + TEST_CASE (4294967297, 1), + TEST_CASE (-4294967297, 4294967295) +#undef TEST_CASE + }; + + for (uint32_t i = 0; + i < sizeof (test_cases_uint32) / sizeof (test_cases_uint32[0]); + i++) + { + JERRY_ASSERT (ecma_number_to_uint32 (test_cases_uint32[i].num) == test_cases_uint32[i].uint32_num); + } + + int32_test_case_t test_cases_int32[] = + { +#define TEST_CASE(num, int32) { num, int32 } + TEST_CASE (1.0, 1), + TEST_CASE (0.0, 0), + TEST_CASE (ecma_number_negate (0.0), 0), + TEST_CASE (NAN, 0), + TEST_CASE (-NAN, 0), + TEST_CASE (INFINITY, 0), + TEST_CASE (-INFINITY, 0), + TEST_CASE (0.1, 0), + TEST_CASE (-0.1, 0), + TEST_CASE (1.1, 1), + TEST_CASE (-1.1, -1), + TEST_CASE (4294967295, -1), + TEST_CASE (-4294967295, 1), + TEST_CASE (4294967296, 0), + TEST_CASE (-4294967296, 0), + TEST_CASE (4294967297, 1), + TEST_CASE (-4294967297, -1), + TEST_CASE (2147483648, -2147483648), + TEST_CASE (-2147483648, -2147483648), + TEST_CASE (2147483647, 2147483647), + TEST_CASE (-2147483647, -2147483647), + TEST_CASE (-2147483649, 2147483647), + TEST_CASE (2147483649, -2147483647) +#undef TEST_CASE + }; + + for (uint32_t i = 0; + i < sizeof (test_cases_int32) / sizeof (test_cases_int32[0]); + i++) + { + JERRY_ASSERT (ecma_number_to_int32 (test_cases_int32[i].num) == test_cases_int32[i].int32_num); + } + + return 0; +} /* main */