From 9d4f7c917fbbbdea3e5b86aa461a9fb80a054e5a Mon Sep 17 00:00:00 2001 From: Akos Kiss Date: Wed, 10 Aug 2016 16:24:12 +0200 Subject: [PATCH] Fixing Math.pow The Math.pow implementation relies on libm's pow. However, the ISO C and ES5.1 standards differ on pow: * `x ** NAN` is NAN in ES but `+1 ** y` is 1 in C * `+-1 ** +-INF` is NAN in ES but 1 in C This patch: * Modifies the Math.pow implementation to handle the special cases instead calling pow. * Adds a test case to jerry-test-suite as it did not test `Math.pow(1,NaN)`. * Fixes jerry-libm's pow, as it was not standard conforming, which helped hiding the error in Math.pow. * Updates the unit test for libm. JerryScript-DCO-1.0-Signed-off-by: Akos Kiss akiss@inf.u-szeged.hu --- .../ecma/builtin-objects/ecma-builtin-math.c | 11 ++++++++++- jerry-libm/pow.c | 13 ++++++++++--- .../15.08.02/15.08.02.13/15.08.02.13-031.js | 16 ++++++++++++++++ tests/unit/test-libm.inc.h | 5 +++++ tools/unit-tests/gen-test-libm.c | 2 -- 5 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 tests/jerry-test-suite/15/15.08/15.08.02/15.08.02.13/15.08.02.13-031.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-math.c b/jerry-core/ecma/builtin-objects/ecma-builtin-math.c index 9a0079198..07d4608af 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-math.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-math.c @@ -477,7 +477,16 @@ ecma_builtin_math_object_pow (ecma_value_t this_arg, /**< 'this' argument */ ECMA_OP_TO_NUMBER_TRY_CATCH (x, arg1, ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (y, arg2, ret_value); - ret_value = ecma_make_number_value (DOUBLE_TO_ECMA_NUMBER_T (pow (x, y))); + if (ecma_number_is_nan (y) || + (ecma_number_is_infinity (y) && (x == 1.0 || x == -1.0))) + { + /* Handle differences between ES5.1 and ISO C standards for pow. */ + ret_value = ecma_make_number_value (ecma_number_make_nan ()); + } + else + { + ret_value = ecma_make_number_value (DOUBLE_TO_ECMA_NUMBER_T (pow (x, y))); + } ECMA_OP_TO_NUMBER_FINALIZE (y); ECMA_OP_TO_NUMBER_FINALIZE (x); diff --git a/jerry-libm/pow.c b/jerry-libm/pow.c index c99183b6e..e58a92dfa 100644 --- a/jerry-libm/pow.c +++ b/jerry-libm/pow.c @@ -39,6 +39,7 @@ * 3. Return x**y = 2**n*exp(y'*log2) * * Special cases: + * 0. +1 ** (anything) is 1 * 1. (anything) ** 0 is 1 * 2. (anything) ** 1 is itself * 3. (anything) ** NAN is NAN @@ -47,7 +48,7 @@ * 6. +-(|x| > 1) ** -INF is +0 * 7. +-(|x| < 1) ** +INF is +0 * 8. +-(|x| < 1) ** -INF is +INF - * 9. +-1 ** +-INF is NAN + * 9. -1 ** +-INF is 1 * 10. +0 ** (+anything except 0, NAN) is +0 * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 * 12. +0 ** (-anything except 0, NAN) is +INF @@ -133,6 +134,12 @@ pow (double x, double y) ix = hx & 0x7fffffff; iy = hy & 0x7fffffff; + /* x == one: 1**y = 1 */ + if (((hx - 0x3ff00000) | lx) == 0) + { + return one; + } + /* y == zero: x**0 = 1 */ if ((iy | ly) == 0) { @@ -184,9 +191,9 @@ pow (double x, double y) { if (iy == 0x7ff00000) /* y is +-inf */ { - if (((ix - 0x3ff00000) | lx) == 0) /* inf**+-1 is NaN */ + if (((ix - 0x3ff00000) | lx) == 0) /* +-1**+-inf is 1 */ { - return y - y; + return one; } else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */ { diff --git a/tests/jerry-test-suite/15/15.08/15.08.02/15.08.02.13/15.08.02.13-031.js b/tests/jerry-test-suite/15/15.08/15.08.02/15.08.02.13/15.08.02.13-031.js new file mode 100644 index 000000000..42d6b1894 --- /dev/null +++ b/tests/jerry-test-suite/15/15.08/15.08.02/15.08.02.13/15.08.02.13-031.js @@ -0,0 +1,16 @@ +// Copyright 2016 Samsung Electronics Co., Ltd. +// Copyright 2016 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. + +assert(isNaN(Math.pow(1, NaN))); diff --git a/tests/unit/test-libm.inc.h b/tests/unit/test-libm.inc.h index 775d6d03b..f1103e416 100644 --- a/tests/unit/test-libm.inc.h +++ b/tests/unit/test-libm.inc.h @@ -330,6 +330,11 @@ check_double ("pow (1.0, 1.0)", pow (1.0, 1.0), 1.00000000000000000000E+00); check_double ("pow (1.0, -1.0)", pow (1.0, -1.0), 1.00000000000000000000E+00); check_double ("pow (-1.0, 1.0)", pow (-1.0, 1.0), -1.00000000000000000000E+00); check_double ("pow (-1.0, -1.0)", pow (-1.0, -1.0), -1.00000000000000000000E+00); +check_double ("pow (1.0, INFINITY)", pow (1.0, INFINITY), 1.00000000000000000000E+00); +check_double ("pow (1.0, -INFINITY)", pow (1.0, -INFINITY), 1.00000000000000000000E+00); +check_double ("pow (-1.0, INFINITY)", pow (-1.0, INFINITY), 1.00000000000000000000E+00); +check_double ("pow (-1.0, -INFINITY)", pow (-1.0, -INFINITY), 1.00000000000000000000E+00); +check_double ("pow (1.0, NAN)", pow (1.0, NAN), 1.00000000000000000000E+00); check_double ("pow (-1.0, NAN)", pow (-1.0, NAN), NAN); check_double ("pow (INFINITY, 0.0)", pow (INFINITY, 0.0), 1.00000000000000000000E+00); check_double ("pow (INFINITY, -0.0)", pow (INFINITY, -0.0), 1.00000000000000000000E+00); diff --git a/tools/unit-tests/gen-test-libm.c b/tools/unit-tests/gen-test-libm.c index 433b1f04f..dbb339c73 100644 --- a/tools/unit-tests/gen-test-libm.c +++ b/tools/unit-tests/gen-test-libm.c @@ -465,13 +465,11 @@ main (int argc, char **args) GEN_DBL_TEST (pow (1.0, -1.0)); GEN_DBL_TEST (pow (-1.0, 1.0)); GEN_DBL_TEST (pow (-1.0, -1.0)); - /* SKIPPED: reference libm implementation seems to (incorrectly?) result 1.0 instead of NAN GEN_DBL_TEST (pow (1.0, INFINITY)); GEN_DBL_TEST (pow (1.0, -INFINITY)); GEN_DBL_TEST (pow (-1.0, INFINITY)); GEN_DBL_TEST (pow (-1.0, -INFINITY)); GEN_DBL_TEST (pow (1.0, NAN)); - */ GEN_DBL_TEST (pow (-1.0, NAN)); GEN_DBL_TEST (pow (INFINITY, 0.0)); GEN_DBL_TEST (pow (INFINITY, -0.0));