]> git.kaiwu.me - njs.git/commitdiff
Fixed Math.round() according to the specification.
authorArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Sat, 9 Nov 2019 22:35:02 +0000 (01:35 +0300)
committerArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Sat, 9 Nov 2019 22:35:02 +0000 (01:35 +0300)
This closes #246 issue on Github.

src/njs_diyfp.h
src/njs_main.h
src/njs_math.c
src/njs_number.c
src/test/njs_unit_test.c

index bcfa6384210a8b07ff04a9d47982fed70b6211c6..3a91c4322afe48013c9130f595b0421bc2082951 100644 (file)
@@ -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;
 }
 
 
index cd2c966339f4393f286135e356bd3f5a76bdb28e..7ecb8b7dc3e168f642f6fdda966b47352d71ac0b 100644 (file)
@@ -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>
index 42201ff747576247a4d084a21f402bb5087f641e..edeb1422274f9333f7c3256312d798c6177902c5 100644 (file)
@@ -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;
 }
index 48183b701c16d36b94497cd134a1a3fc16c940c7..175fa2127691c259186877900d24b6f66260387e 100644 (file)
@@ -6,7 +6,6 @@
 
 
 #include <njs_main.h>
-#include <njs_diyfp.h>
 
 
 /*
index 4412f658dce5de0f10ee4ed23ca4c0016af02b32..da46420f5aa94261b0e9e080fc3889daaef9529a 100644 (file)
@@ -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") },