Browse Source

Merge pull request #1099 from svaarala/fix-cygwin-math-issues

Fix a few math issues on Cygwin (MinGW)
pull/1102/head
Sami Vaarala 8 years ago
committed by GitHub
parent
commit
23bd6e7142
  1. 21
      Makefile
  2. 3
      RELEASES.rst
  3. 8
      config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml
  4. 1
      config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml
  5. 9
      config/config-options/DUK_USE_POW_WORKAROUNDS.yaml
  6. 19
      config/header-snippets/platform_fillins.h.in
  7. 3
      doc/release-notes-v2-0.rst
  8. 33
      src-input/duk_bi_math.c
  9. 1
      src-input/duk_js.h
  10. 77
      src-input/duk_js_arith.c
  11. 10
      src-input/duk_js_executor.c
  12. 75
      tests/ecmascript/test-bug-mingw-math-issues.js
  13. 2
      util/makeduk_base.yaml
  14. 2
      util/makeduk_debug.yaml

21
Makefile

@ -59,13 +59,20 @@ DUKTAPE_CMDLINE_SOURCES = \
examples/alloc-logging/duk_alloc_logging.c \
examples/alloc-torture/duk_alloc_torture.c \
examples/alloc-hybrid/duk_alloc_hybrid.c \
examples/debug-trans-socket/duk_trans_socket_unix.c \
extras/print-alert/duk_print_alert.c \
extras/console/duk_console.c \
extras/logging/duk_logging.c \
extras/module-duktape/duk_module_duktape.c
LINENOISE_SOURCES = \
linenoise/linenoise.c
ifdef SYSTEMROOT # Windows
DUKTAPE_CMDLINE_SOURCES += examples/debug-trans-socket/duk_trans_socket_windows.c
else
DUKTAPE_CMDLINE_SOURCES += examples/debug-trans-socket/duk_trans_socket_unix.c
endif
ifdef SYSTEMROOT # Windows
LINENOISE_SOURCES =
else
LINENOISE_SOURCES = linenoise/linenoise.c
endif
# Configure.py options for a few configuration profiles needed.
CONFIGOPTS_NONDEBUG=--option-file util/makeduk_base.yaml
@ -90,7 +97,11 @@ CCOPTS_SHARED += -DDUK_CMDLINE_PRINTALERT_SUPPORT
CCOPTS_SHARED += -DDUK_CMDLINE_CONSOLE_SUPPORT
CCOPTS_SHARED += -DDUK_CMDLINE_LOGGING_SUPPORT
CCOPTS_SHARED += -DDUK_CMDLINE_MODULE_SUPPORT
ifdef SYSTEMROOT # Windows
# Skip fancy (linenoise)
else
CCOPTS_SHARED += -DDUK_CMDLINE_FANCY
endif
CCOPTS_SHARED += -DDUK_CMDLINE_ALLOC_LOGGING
CCOPTS_SHARED += -DDUK_CMDLINE_ALLOC_TORTURE
CCOPTS_SHARED += -DDUK_CMDLINE_ALLOC_HYBRID
@ -140,7 +151,11 @@ CCOPTS_AJDUK += -UDUK_CMDLINE_FANCY -DDUK_CMDLINE_AJSHEAP -D_POSIX_C_SOURCE=2008
CCOPTS_AJDUK += -UDUK_CMDLINE_LOGGING_SUPPORT # extras/logger init writes to Duktape.Logger, problem with ROM build
CCOPTS_AJDUK += -UDUK_CMDLINE_MODULE_SUPPORT # extras/module-duktape init writes to Duktape.Logger, problem with ROM build
ifdef SYSTEMROOT # Windows
CCLIBS = -lm -lws2_32
else
CCLIBS = -lm
endif
# Emscripten options:
# - --memory-init-file 0 to avoid a separate memory init file (this is

3
RELEASES.rst

@ -2033,6 +2033,9 @@ Planned
* Fix Cygwin warning about shadowed 'accept' variable (GH-1098)
* Fix Cygwin/MinGW math issues related to pow2() and atan2() semantics
(GH-1099)
* Fix two-argument Math function (like Math.atan2()) argument coercion
order; the order was not guaranteed but specification requires left-to-right
ordering (GH-943)

8
config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml

@ -0,0 +1,8 @@
define: DUK_USE_ATAN2_WORKAROUNDS
introduced: 2.0.0
default: false
tags:
- portability
description: >
Enable workarounds to common atan2() semantics issues. At least Cygwin/MinGW
has such issues, see test-bug-mingw-math-issues.js.

1
config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml

@ -1,5 +1,6 @@
define: DUK_USE_POW_NETBSD_WORKAROUND
introduced: 1.0.0
removed: 2.0.0
default: false
tags:
- portability

9
config/config-options/DUK_USE_POW_WORKAROUNDS.yaml

@ -0,0 +1,9 @@
define: DUK_USE_POW_WORKAROUNDS
introduced: 2.0.0
default: false
tags:
- portability
description: >
Enable workarounds to common pow() semantics issues. At least NetBSD
6.0 x86 and Cygwin/MinGW have such issues, see test-bug-netbsd-math-pow.js
and test-bug-mingw-math-issues.js.

19
config/header-snippets/platform_fillins.h.in

@ -279,12 +279,21 @@
#endif /* DUK_F_C99 */
/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics,
* see test-bug-netbsd-math-pow.js. Use NetBSD specific workaround.
* (This might be a wider problem; if so, generalize the define name.)
* see test-bug-netbsd-math-pow.js. MinGW has similar (but different)
* issues, see test-bug-mingw-math-issues.js. Enable pow() workarounds
* for these targets.
*/
#undef DUK_USE_POW_NETBSD_WORKAROUND
#if defined(DUK_F_NETBSD)
#define DUK_USE_POW_NETBSD_WORKAROUND
#undef DUK_USE_POW_WORKAROUNDS
#if defined(DUK_F_NETBSD) || defined(DUK_F_MINGW)
#define DUK_USE_POW_WORKAROUNDS
#endif
/* Similar workarounds for atan2() semantics issues. MinGW issues are
* documented in test-bug-mingw-math-issues.js.
*/
#undef DUK_USE_ATAN2_WORKAROUNDS
#if defined(DUK_F_MINGW)
#define DUK_USE_ATAN2_WORKAROUNDS
#endif
/* Rely as little as possible on compiler behavior for NaN comparison,

3
doc/release-notes-v2-0.rst

@ -1030,6 +1030,9 @@ Other incompatible changes
is the same as "\u00078", "\8" and "\9" are accepted as literal "8" and "9"
(even in strict mode).
* The NetBSD pow() workaround option ``DUK_USE_POW_NETBSD_WORKAROUND`` has been
generalized and renamed to ``DUK_USE_POW_WORKAROUNDS``.
Known issues
============

33
src-input/duk_bi_math.c

@ -225,7 +225,34 @@ DUK_LOCAL double duk__sqrt(double x) {
DUK_LOCAL double duk__tan(double x) {
return DUK_TAN(x);
}
DUK_LOCAL double duk__atan2(double x, double y) {
DUK_LOCAL double duk__atan2_fixed(double x, double y) {
#if defined(DUK_USE_ATAN2_WORKAROUNDS)
/* Specific fixes to common atan2() implementation issues:
* - test-bug-mingw-math-issues.js
*/
if (DUK_ISINF(x) && DUK_ISINF(y)) {
if (DUK_SIGNBIT(x)) {
if (DUK_SIGNBIT(y)) {
return -2.356194490192345;
} else {
return -0.7853981633974483;
}
} else {
if (DUK_SIGNBIT(y)) {
return 2.356194490192345;
} else {
return 0.7853981633974483;
}
}
}
#else
/* Some ISO C assumptions. */
DUK_ASSERT(DUK_ATAN2(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY) == 0.7853981633974483);
DUK_ASSERT(DUK_ATAN2(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY) == -0.7853981633974483);
DUK_ASSERT(DUK_ATAN2(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY) == 2.356194490192345);
DUK_ASSERT(DUK_ATAN2(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY) == -2.356194490192345);
#endif
return DUK_ATAN2(x, y);
}
#endif /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */
@ -278,10 +305,10 @@ DUK_LOCAL const duk__one_arg_func duk__one_arg_funcs[] = {
/* order must match constants in genbuiltins.py */
DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = {
#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
duk__atan2,
duk__atan2_fixed,
duk_js_arith_pow
#else
DUK_ATAN2,
duk__atan2_fixed,
duk_js_arith_pow
#endif
};

1
src-input/duk_js.h

@ -43,6 +43,7 @@ DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);
/* arithmetic */
DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y);
DUK_INTERNAL_DECL double duk_js_arith_mod(double x, double y);
#define duk_js_equals(thr,tv_x,tv_y) \
duk_js_equals_helper((thr), (tv_x), (tv_y), 0)

77
src-input/duk_js_arith.c

@ -4,6 +4,58 @@
#include "duk_internal.h"
/* Ecmascript modulus ('%') does not match IEEE 754 "remainder" operation
* (implemented by remainder() in C99) but does seem to match ANSI C fmod().
* Compare E5 Section 11.5.3 and "man fmod".
*/
DUK_INTERNAL double duk_js_arith_mod(double d1, double d2) {
#if defined(DUK_USE_POW_WORKAROUNDS)
/* Specific fixes to common fmod() implementation issues:
* - test-bug-mingw-math-issues.js
*/
if (DUK_ISINF(d2)) {
if (DUK_ISINF(d1)) {
return DUK_DOUBLE_NAN;
} else {
return d1;
}
} else if (d1 == 0.0) {
/* d1 +/-0 is returned as is (preserving sign) except when
* d2 is zero or NaN.
*/
if (d2 == 0.0 || DUK_ISNAN(d2)) {
return DUK_DOUBLE_NAN;
} else {
return d1;
}
}
#else
/* Some ISO C assumptions. */
DUK_ASSERT(DUK_FMOD(1.0, DUK_DOUBLE_INFINITY) == 1.0);
DUK_ASSERT(DUK_FMOD(-1.0, DUK_DOUBLE_INFINITY) == -1.0);
DUK_ASSERT(DUK_FMOD(1.0, -DUK_DOUBLE_INFINITY) == 1.0);
DUK_ASSERT(DUK_FMOD(-1.0, -DUK_DOUBLE_INFINITY) == -1.0);
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_FMOD(0.0, 1.0) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, 1.0)) == 0);
DUK_ASSERT(DUK_FMOD(-0.0, 1.0) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, 1.0)) != 0);
DUK_ASSERT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
DUK_ASSERT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY)) != 0);
DUK_ASSERT(DUK_FMOD(0.0, -DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
DUK_ASSERT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY)) != 0);
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, 0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, 0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, -0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, -0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, DUK_DOUBLE_NAN)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, DUK_DOUBLE_NAN)));
#endif
return (duk_double_t) DUK_FMOD((double) d1, (double) d2);
}
/* Shared helper for Math.pow() and exponentiation operator. */
DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
/* The ANSI C pow() semantics differ from Ecmascript.
@ -24,10 +76,11 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
if (DUK_FABS(x) == 1.0 && cy == DUK_FP_INFINITE) {
goto ret_nan;
}
#if defined(DUK_USE_POW_NETBSD_WORKAROUND)
/* See test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least) does not
* correctly handle some cases where x=+/-0. Specific fixes to these
* here.
#if defined(DUK_USE_POW_WORKAROUNDS)
/* Specific fixes to common pow() implementation issues:
* - test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least)
* - test-bug-mingw-math-issues.js
*/
cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (cx == DUK_FP_ZERO && y < 0.0) {
@ -42,7 +95,7 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
} else {
/* Math.pow(-0,y) where y<0 should be:
* - -Infinity if y<0 and an odd integer
* - Infinity otherwise
* - Infinity if y<0 but not an odd integer
* NetBSD pow() returns -Infinity for all finite y<0. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
@ -61,8 +114,22 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
return DUK_DOUBLE_INFINITY;
}
}
} else if (cx == DUK_FP_NAN) {
if (y == 0.0) {
/* NaN ** +/- 0 should always be 1, but is NaN on
* at least some Cygwin/MinGW versions.
*/
return 1.0;
}
}
#else
/* Some ISO C assumptions. */
DUK_ASSERT(DUK_POW(DUK_DOUBLE_NAN, 0.0) == 1.0);
DUK_ASSERT(DUK_ISINF(DUK_POW(0.0, -1.0)) && DUK_SIGNBIT(DUK_POW(0.0, -1.0)) == 0);
DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -2.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -2.0)) == 0);
DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -3.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -3.0)) != 0);
#endif
return DUK_POW(x, y);
ret_nan:

10
src-input/duk_js_executor.c

@ -73,15 +73,7 @@ DUK_LOCAL void duk__push_tvals_incref_only(duk_hthread *thr, duk_tval *tv_src, d
*/
DUK_LOCAL DUK__INLINE_PERF duk_double_t duk__compute_mod(duk_double_t d1, duk_double_t d2) {
/*
* Ecmascript modulus ('%') does not match IEEE 754 "remainder"
* operation (implemented by remainder() in C99) but does seem
* to match ANSI C fmod().
*
* Compare E5 Section 11.5.3 and "man fmod".
*/
return (duk_double_t) DUK_FMOD((double) d1, (double) d2);
return (duk_double_t) duk_js_arith_mod((double) d1, (double) d2);
}
#if defined(DUK_USE_ES7_EXP_OPERATOR)

75
tests/ecmascript/test-bug-mingw-math-issues.js

@ -0,0 +1,75 @@
/*
* Some MinGW math issues
*
* https://github.com/svaarala/duktape/pull/1099
*/
/*@include util-number.js@*/
/*===
1
1
-1e+100
-1e+100
NaN
NaN
NaN
NaN
0
-0
0
-0
0
-0
0
-0
NaN
NaN
NaN
NaN
NaN
NaN
0.7853981633974483
2.356194490192345
-0.7853981633974483
-2.356194490192345
1
1
===*/
function test() {
printExact((0/0) ** 0);
printExact((0/0) ** -0);
printExact((-1e100) % (1/0)); // return 1st arg if modulus is +/- inf
printExact((-1e100) % (-1/0));
printExact((1/0) % (1/0)); // but if 1st arg is +/- inf (and modulus +/- inf), return NaN
printExact((1/0) % (-1/0));
printExact((-1/0) % (1/0));
printExact((-1/0) % (-1/0));
printExact(0 % 1); // preserve zero and its sign, even for inf
printExact(-0 % 1);
printExact(0 % -1);
printExact(-0 % -1);
printExact(0 % (1/0));
printExact(-0 % (1/0));
printExact(0 % (-1/0));
printExact(-0 % (-1/0));
printExact(0 % (0/0)); // ... but if modulus is NaN or 0, result is NaN
printExact(-0 % (0/0));
printExact(0 % 0);
printExact(-0 % 0);
printExact(0 % -0);
printExact(-0 % -0);
printExact(Math.atan2(1/0, 1/0));
printExact(Math.atan2(1/0, -1/0));
printExact(Math.atan2(-1/0, 1/0));
printExact(Math.atan2(-1/0, -1/0));
printExact(Math.pow(0/0, 0));
printExact(Math.pow(0/0, -0));
}
try {
test();
} catch (e) {
print(e.stack || e);
}

2
util/makeduk_base.yaml

@ -8,7 +8,7 @@
DUK_USE_TARGET_INFO: "\"duk command built from Duktape repo\""
DUK_USE_FATAL_HANDLER:
verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) do { const char *fatal_msg = (msg); fprintf(stderr, \"*** FATAL ERROR: %s\\n\", fatal_msg ? fatal_msg : \"no message\"); *((volatile unsigned int *) 0) = (unsigned int) 0xdeadbeefUL; abort(); } while(0)"
verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) do { const char *fatal_msg = (msg); fprintf(stderr, \"*** FATAL ERROR: %s\\n\", fatal_msg ? fatal_msg : \"no message\"); fflush(stderr); *((volatile unsigned int *) 0) = (unsigned int) 0xdeadbeefUL; abort(); } while(0)"
DUK_USE_SELF_TESTS: true
#DUK_USE_ASSERTIONS: true

2
util/makeduk_debug.yaml

@ -5,5 +5,5 @@ DUK_USE_DEBUG: true
DUK_USE_DEBUG_LEVEL: 0
#DUK_USE_DEBUG_LEVEL: 1
DUK_USE_DEBUG_WRITE:
verbatim: "#define DUK_USE_DEBUG_WRITE(level,file,line,func,msg) do {fprintf(stderr, \"D%ld %s:%ld (%s): %s\\n\", (long) (level), (file), (long) (line), (func), (msg));} while(0)"
verbatim: "#define DUK_USE_DEBUG_WRITE(level,file,line,func,msg) do {fprintf(stderr, \"D%ld %s:%ld (%s): %s\\n\", (long) (level), (file), (long) (line), (func), (msg)); fflush(stderr);} while(0)"
DUK_USE_ASSERTIONS: true

Loading…
Cancel
Save