[njs] Fixed Math.round() according to the specification.
Dmitry Volyntsev
xeioex at nginx.com
Mon Nov 11 17:24:01 UTC 2019
details: https://hg.nginx.org/njs/rev/fdd56914c9ca
branches:
changeset: 1232:fdd56914c9ca
user: Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date: Sun Nov 10 01:35:02 2019 +0300
description:
Fixed Math.round() according to the specification.
This closes #246 issue on Github.
diffstat:
src/njs_diyfp.h | 31 ++++++++++++++++++-------------
src/njs_main.h | 1 +
src/njs_math.c | 47 ++++++++++++++++++++++++++++++++++++++++++++---
src/njs_number.c | 1 -
src/test/njs_unit_test.c | 18 ++++++++++++++++++
5 files changed, 81 insertions(+), 17 deletions(-)
diffs (193 lines):
diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_diyfp.h
--- a/src/njs_diyfp.h Thu Oct 31 22:18:41 2019 +0300
+++ b/src/njs_diyfp.h Sun Nov 10 01:35:02 2019 +0300
@@ -18,25 +18,34 @@ typedef struct {
} njs_diyfp_t;
+typedef union {
+ double d;
+ uint64_t u64;
+} njs_diyfp_conv_t;
+
+
#define njs_diyfp(_s, _e) (njs_diyfp_t) \
{ .significand = (_s), .exp = (_e) }
#define njs_uint64(h, l) (((uint64_t) (h) << 32) + (l))
#define NJS_DBL_SIGNIFICAND_SIZE 52
-#define NJS_DBL_EXPONENT_BIAS (0x3FF + NJS_DBL_SIGNIFICAND_SIZE)
+#define NJS_DBL_EXPONENT_OFFSET ((int64_t) 0x3ff)
+#define NJS_DBL_EXPONENT_BIAS (NJS_DBL_EXPONENT_OFFSET \
+ + NJS_DBL_SIGNIFICAND_SIZE)
#define NJS_DBL_EXPONENT_MIN (-NJS_DBL_EXPONENT_BIAS)
-#define NJS_DBL_EXPONENT_MAX (0x7FF - NJS_DBL_EXPONENT_BIAS)
+#define NJS_DBL_EXPONENT_MAX (0x7ff - NJS_DBL_EXPONENT_BIAS)
#define NJS_DBL_EXPONENT_DENORMAL (-NJS_DBL_EXPONENT_BIAS + 1)
#define NJS_DBL_SIGNIFICAND_MASK njs_uint64(0x000FFFFF, 0xFFFFFFFF)
#define NJS_DBL_HIDDEN_BIT njs_uint64(0x00100000, 0x00000000)
#define NJS_DBL_EXPONENT_MASK njs_uint64(0x7FF00000, 0x00000000)
+#define NJS_DBL_SIGN_MASK njs_uint64(0x80000000, 0x00000000)
#define NJS_DIYFP_SIGNIFICAND_SIZE 64
#define NJS_SIGNIFICAND_SIZE 53
-#define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \
+#define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \
- NJS_DBL_SIGNIFICAND_SIZE)
#define NJS_DECIMAL_EXPONENT_OFF 348
@@ -78,13 +87,9 @@ njs_d2diyfp(double d)
njs_inline double
njs_diyfp2d(njs_diyfp_t v)
{
- int exp;
- uint64_t significand, biased_exp;
-
- union {
- double d;
- uint64_t u64;
- } u;
+ int exp;
+ uint64_t significand, biased_exp;
+ njs_diyfp_conv_t conv;
exp = v.exp;
significand = v.significand;
@@ -118,10 +123,10 @@ njs_diyfp2d(njs_diyfp_t v)
biased_exp = (uint64_t) (exp + NJS_DBL_EXPONENT_BIAS);
}
- u.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK)
- | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE);
+ conv.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK)
+ | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE);
- return u.d;
+ return conv.d;
}
diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_main.h
--- a/src/njs_main.h Thu Oct 31 22:18:41 2019 +0300
+++ b/src/njs_main.h Sun Nov 10 01:35:02 2019 +0300
@@ -15,6 +15,7 @@
#include <njs_clang.h>
#include <njs_str.h>
#include <njs_utf8.h>
+#include <njs_diyfp.h>
#include <njs_dtoa.h>
#include <njs_dtoa_fixed.h>
#include <njs_strtod.h>
diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_math.c
--- a/src/njs_math.c Thu Oct 31 22:18:41 2019 +0300
+++ b/src/njs_math.c Sun Nov 10 01:35:02 2019 +0300
@@ -746,21 +746,62 @@ static njs_int_t
njs_object_math_round(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- njs_int_t ret;
+ uint8_t sign;
+ uint64_t one, fraction_mask;
+ njs_int_t ret, biased_exp;
+ njs_diyfp_conv_t conv;
if (njs_slow_path(nargs < 2)) {
njs_set_number(&vm->retval, NAN);
return NJS_OK;
}
- if (njs_slow_path(!njs_is_number(&args[1]))) {
+ if (njs_slow_path(!njs_is_numeric(&args[1]))) {
ret = njs_value_to_numeric(vm, &args[1], &args[1]);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
- njs_set_number(&vm->retval, round(njs_number(&args[1])));
+ conv.d = njs_number(&args[1]);
+ biased_exp = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE;
+
+ if (biased_exp < NJS_DBL_EXPONENT_OFFSET) {
+
+ /* |v| < 1. */
+
+ if (biased_exp == (NJS_DBL_EXPONENT_OFFSET - 1)
+ && conv.u64 != njs_uint64(0xbfe00000, 0x00000000))
+ {
+ /* (|v| > 0.5 || v == 0.5) => +-1.0 */
+
+ conv.u64 = (conv.u64 & NJS_DBL_SIGN_MASK)
+ | (NJS_DBL_EXPONENT_OFFSET << NJS_DBL_SIGNIFICAND_SIZE);
+
+ } else {
+
+ /* (|v| < 0.5 || v == -0.5) => +-0. */
+
+ conv.u64 &= ((uint64_t) 1) << 63;
+ }
+
+ } else if (biased_exp < NJS_DBL_EXPONENT_BIAS) {
+
+ /* |v| <= 2^52 - 1 (largest safe integer). */
+
+ one = ((uint64_t) 1) << (NJS_DBL_EXPONENT_BIAS - biased_exp);
+ fraction_mask = one - 1;
+
+ /* truncation. */
+
+ sign = conv.u64 >> 63;
+ conv.u64 += (one >> 1) - sign;
+ conv.u64 &= ~fraction_mask;
+ }
+
+ /* |v| >= 2^52, Infinity and NaNs => v. */
+
+ njs_set_number(&vm->retval, conv.d);
return NJS_OK;
}
diff -r 0d119e8ce71a -r fdd56914c9ca src/njs_number.c
--- a/src/njs_number.c Thu Oct 31 22:18:41 2019 +0300
+++ b/src/njs_number.c Sun Nov 10 01:35:02 2019 +0300
@@ -6,7 +6,6 @@
#include <njs_main.h>
-#include <njs_diyfp.h>
/*
diff -r 0d119e8ce71a -r fdd56914c9ca src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Thu Oct 31 22:18:41 2019 +0300
+++ b/src/test/njs_unit_test.c Sun Nov 10 01:35:02 2019 +0300
@@ -12995,8 +12995,26 @@ static njs_unit_test_t njs_test[] =
njs_str("-0") },
{ njs_str("Math.round(-0.5)"),
+ njs_str("-0") },
+
+ { njs_str("Math.round(-0.50000000000000001)"),
+ njs_str("-0") },
+
+ { njs_str("Math.round(-0.5000000000000001)"),
njs_str("-1") },
+ { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) + v) - 2**32)"),
+ njs_str("1,1,1") },
+
+ { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) + v) + 2**32)"),
+ njs_str("1,1,1") },
+
+ { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) - v) - 2**32)"),
+ njs_str("-1,0,0") },
+
+ { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) - v) + 2**32)"),
+ njs_str("-1,0,0") },
+
{ njs_str("Math.sign(5)"),
njs_str("1") },
More information about the nginx-devel
mailing list