Skip to content

Commit 9d4f7c9

Browse files
committed
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 [email protected]
1 parent 8f76bab commit 9d4f7c9

File tree

5 files changed

+41
-6
lines changed

5 files changed

+41
-6
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-math.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,16 @@ ecma_builtin_math_object_pow (ecma_value_t this_arg, /**< 'this' argument */
477477
ECMA_OP_TO_NUMBER_TRY_CATCH (x, arg1, ret_value);
478478
ECMA_OP_TO_NUMBER_TRY_CATCH (y, arg2, ret_value);
479479

480-
ret_value = ecma_make_number_value (DOUBLE_TO_ECMA_NUMBER_T (pow (x, y)));
480+
if (ecma_number_is_nan (y) ||
481+
(ecma_number_is_infinity (y) && (x == 1.0 || x == -1.0)))
482+
{
483+
/* Handle differences between ES5.1 and ISO C standards for pow. */
484+
ret_value = ecma_make_number_value (ecma_number_make_nan ());
485+
}
486+
else
487+
{
488+
ret_value = ecma_make_number_value (DOUBLE_TO_ECMA_NUMBER_T (pow (x, y)));
489+
}
481490

482491
ECMA_OP_TO_NUMBER_FINALIZE (y);
483492
ECMA_OP_TO_NUMBER_FINALIZE (x);

jerry-libm/pow.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* 3. Return x**y = 2**n*exp(y'*log2)
4040
*
4141
* Special cases:
42+
* 0. +1 ** (anything) is 1
4243
* 1. (anything) ** 0 is 1
4344
* 2. (anything) ** 1 is itself
4445
* 3. (anything) ** NAN is NAN
@@ -47,7 +48,7 @@
4748
* 6. +-(|x| > 1) ** -INF is +0
4849
* 7. +-(|x| < 1) ** +INF is +0
4950
* 8. +-(|x| < 1) ** -INF is +INF
50-
* 9. +-1 ** +-INF is NAN
51+
* 9. -1 ** +-INF is 1
5152
* 10. +0 ** (+anything except 0, NAN) is +0
5253
* 11. -0 ** (+anything except 0, NAN, odd integer) is +0
5354
* 12. +0 ** (-anything except 0, NAN) is +INF
@@ -133,6 +134,12 @@ pow (double x, double y)
133134
ix = hx & 0x7fffffff;
134135
iy = hy & 0x7fffffff;
135136

137+
/* x == one: 1**y = 1 */
138+
if (((hx - 0x3ff00000) | lx) == 0)
139+
{
140+
return one;
141+
}
142+
136143
/* y == zero: x**0 = 1 */
137144
if ((iy | ly) == 0)
138145
{
@@ -184,9 +191,9 @@ pow (double x, double y)
184191
{
185192
if (iy == 0x7ff00000) /* y is +-inf */
186193
{
187-
if (((ix - 0x3ff00000) | lx) == 0) /* inf**+-1 is NaN */
194+
if (((ix - 0x3ff00000) | lx) == 0) /* +-1**+-inf is 1 */
188195
{
189-
return y - y;
196+
return one;
190197
}
191198
else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */
192199
{
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 Samsung Electronics Co., Ltd.
2+
// Copyright 2016 University of Szeged
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
assert(isNaN(Math.pow(1, NaN)));

tests/unit/test-libm.inc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ check_double ("pow (1.0, 1.0)", pow (1.0, 1.0), 1.00000000000000000000E+00);
330330
check_double ("pow (1.0, -1.0)", pow (1.0, -1.0), 1.00000000000000000000E+00);
331331
check_double ("pow (-1.0, 1.0)", pow (-1.0, 1.0), -1.00000000000000000000E+00);
332332
check_double ("pow (-1.0, -1.0)", pow (-1.0, -1.0), -1.00000000000000000000E+00);
333+
check_double ("pow (1.0, INFINITY)", pow (1.0, INFINITY), 1.00000000000000000000E+00);
334+
check_double ("pow (1.0, -INFINITY)", pow (1.0, -INFINITY), 1.00000000000000000000E+00);
335+
check_double ("pow (-1.0, INFINITY)", pow (-1.0, INFINITY), 1.00000000000000000000E+00);
336+
check_double ("pow (-1.0, -INFINITY)", pow (-1.0, -INFINITY), 1.00000000000000000000E+00);
337+
check_double ("pow (1.0, NAN)", pow (1.0, NAN), 1.00000000000000000000E+00);
333338
check_double ("pow (-1.0, NAN)", pow (-1.0, NAN), NAN);
334339
check_double ("pow (INFINITY, 0.0)", pow (INFINITY, 0.0), 1.00000000000000000000E+00);
335340
check_double ("pow (INFINITY, -0.0)", pow (INFINITY, -0.0), 1.00000000000000000000E+00);

tools/unit-tests/gen-test-libm.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,13 +465,11 @@ main (int argc, char **args)
465465
GEN_DBL_TEST (pow (1.0, -1.0));
466466
GEN_DBL_TEST (pow (-1.0, 1.0));
467467
GEN_DBL_TEST (pow (-1.0, -1.0));
468-
/* SKIPPED: reference libm implementation seems to (incorrectly?) result 1.0 instead of NAN
469468
GEN_DBL_TEST (pow (1.0, INFINITY));
470469
GEN_DBL_TEST (pow (1.0, -INFINITY));
471470
GEN_DBL_TEST (pow (-1.0, INFINITY));
472471
GEN_DBL_TEST (pow (-1.0, -INFINITY));
473472
GEN_DBL_TEST (pow (1.0, NAN));
474-
*/
475473
GEN_DBL_TEST (pow (-1.0, NAN));
476474
GEN_DBL_TEST (pow (INFINITY, 0.0));
477475
GEN_DBL_TEST (pow (INFINITY, -0.0));

0 commit comments

Comments
 (0)