[njs] Fixed Number.toString().

Dmitry Volyntsev xeioex at nginx.com
Thu Jul 19 15:12:57 UTC 2018


details:   http://hg.nginx.org/njs/rev/0f9c060a0198
branches:  
changeset: 561:0f9c060a0198
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Jul 19 18:11:52 2018 +0300
description:
Fixed Number.toString().

Adding correct dtoa() and strtod() realization.

This fixes #28 and #30 issues on GitHub.

diffstat:

 Makefile                 |    6 +
 njs/njs_core.h           |    2 +
 njs/njs_json.c           |    2 +-
 njs/njs_number.c         |  116 +-------------
 njs/njs_number.h         |    1 -
 njs/test/njs_unit_test.c |   74 ++++++-
 nxt/Makefile             |   36 ++++
 nxt/auto/clang           |   26 +++
 nxt/nxt_clang.h          |   32 +++
 nxt/nxt_diyfp.c          |  150 +++++++++++++++++
 nxt/nxt_diyfp.h          |  212 ++++++++++++++++++++++++
 nxt/nxt_dtoa.c           |  363 ++++++++++++++++++++++++++++++++++++++++++
 nxt/nxt_dtoa.h           |   12 +
 nxt/nxt_strtod.c         |  403 +++++++++++++++++++++++++++++++++++++++++++++++
 nxt/nxt_strtod.h         |   12 +
 nxt/nxt_types.h          |    5 +
 16 files changed, 1322 insertions(+), 130 deletions(-)

diffs (truncated from 1692 to 1000 lines):

diff -r 9322e8eab0b6 -r 0f9c060a0198 Makefile
--- a/Makefile	Wed Jul 18 15:41:55 2018 +0300
+++ b/Makefile	Thu Jul 19 18:11:52 2018 +0300
@@ -34,6 +34,9 @@ NXT_BUILDDIR =	build
 	$(NXT_BUILDDIR)/njs_parser_expression.o \
 	$(NXT_BUILDDIR)/njs_generator.o \
 	$(NXT_BUILDDIR)/njs_disassembler.o \
+	$(NXT_BUILDDIR)/nxt_diyfp.o \
+	$(NXT_BUILDDIR)/nxt_dtoa.o \
+	$(NXT_BUILDDIR)/nxt_strtod.o \
 	$(NXT_BUILDDIR)/nxt_djb_hash.o \
 	$(NXT_BUILDDIR)/nxt_utf8.o \
 	$(NXT_BUILDDIR)/nxt_array.o \
@@ -76,6 +79,9 @@ NXT_BUILDDIR =	build
 		$(NXT_BUILDDIR)/njs_parser_expression.o \
 		$(NXT_BUILDDIR)/njs_generator.o \
 		$(NXT_BUILDDIR)/njs_disassembler.o \
+		$(NXT_BUILDDIR)/nxt_diyfp.o \
+		$(NXT_BUILDDIR)/nxt_dtoa.o \
+		$(NXT_BUILDDIR)/nxt_strtod.o \
 		$(NXT_BUILDDIR)/nxt_djb_hash.o \
 		$(NXT_BUILDDIR)/nxt_utf8.o \
 		$(NXT_BUILDDIR)/nxt_array.o \
diff -r 9322e8eab0b6 -r 0f9c060a0198 njs/njs_core.h
--- a/njs/njs_core.h	Wed Jul 18 15:41:55 2018 +0300
+++ b/njs/njs_core.h	Thu Jul 19 18:11:52 2018 +0300
@@ -15,6 +15,8 @@
 #include <nxt_string.h>
 #include <nxt_stub.h>
 #include <nxt_utf8.h>
+#include <nxt_dtoa.h>
+#include <nxt_strtod.h>
 #include <nxt_djb_hash.h>
 #include <nxt_trace.h>
 #include <nxt_array.h>
diff -r 9322e8eab0b6 -r 0f9c060a0198 njs/njs_json.c
--- a/njs/njs_json.c	Wed Jul 18 15:41:55 2018 +0300
+++ b/njs/njs_json.c	Thu Jul 19 18:11:52 2018 +0300
@@ -1844,7 +1844,7 @@ njs_json_append_number(njs_json_stringif
             return NXT_ERROR;
         }
 
-        size = njs_num_to_buf(num, p, 64);
+        size = nxt_dtoa(num, (char *) p);
 
         njs_json_buf_written(stringify, size);
     }
diff -r 9322e8eab0b6 -r 0f9c060a0198 njs/njs_number.c
--- a/njs/njs_number.c	Wed Jul 18 15:41:55 2018 +0300
+++ b/njs/njs_number.c	Thu Jul 19 18:11:52 2018 +0300
@@ -66,91 +66,7 @@ njs_value_to_index(const njs_value_t *va
 double
 njs_number_dec_parse(const u_char **start, const u_char *end)
 {
-    u_char        c;
-    double        num, frac, scale, exponent;
-    nxt_bool_t    minus;
-    const u_char  *e, *p;
-
-    p = *start;
-
-    num = 0;
-
-    while (p < end) {
-        /* Values less than '0' become >= 208. */
-        c = *p - '0';
-
-        if (nxt_slow_path(c > 9)) {
-            break;
-        }
-
-        num = num * 10 + c;
-        p++;
-    }
-
-    if (p < end && *p == '.') {
-
-        frac = 0;
-        scale = 1;
-
-        for (p++; p < end; p++) {
-            /* Values less than '0' become >= 208. */
-            c = *p - '0';
-
-            if (nxt_slow_path(c > 9)) {
-                break;
-            }
-
-            frac = frac * 10 + c;
-            scale *= 10;
-        }
-
-        num += frac / scale;
-    }
-
-    e = p + 1;
-
-    if (e < end && (*p == 'e' || *p == 'E')) {
-        minus = 0;
-
-        if (e + 1 < end) {
-            if (*e == '-') {
-                e++;
-                minus = 1;
-
-            } else if (*e == '+') {
-                e++;
-            }
-        }
-
-        /* Values less than '0' become >= 208. */
-        c = *e - '0';
-
-        if (nxt_fast_path(c <= 9)) {
-            exponent = c;
-            p = e + 1;
-
-            while (p < end) {
-                /* Values less than '0' become >= 208. */
-                c = *p - '0';
-
-                if (nxt_slow_path(c > 9)) {
-                    break;
-                }
-
-                exponent = exponent * 10 + c;
-                p++;
-            }
-
-            if (num != 0) {
-                exponent = minus ? -exponent : exponent;
-                num = num * pow(10.0, exponent);
-            }
-        }
-    }
-
-    *start = p;
-
-    return num;
+    return nxt_strtod(start, end);
 }
 
 
@@ -312,7 +228,7 @@ njs_number_to_string(njs_vm_t *vm, njs_v
         }
 
     } else {
-        size = njs_num_to_buf(num, buf, sizeof(buf));
+        size = nxt_dtoa(num, (char *) buf);
 
         return njs_string_new(vm, string, buf, size, size);
     }
@@ -323,34 +239,6 @@ njs_number_to_string(njs_vm_t *vm, njs_v
 }
 
 
-size_t
-njs_num_to_buf(double num, u_char *buf, size_t size)
-{
-    double      n;
-    const char  *fmt;
-
-    n = fabs(num);
-
-    if (n == 0) {
-        fmt = "%g";
-
-    } else if (n < 1) {
-        fmt = "%f";
-
-    } else if (n < 1000000) {
-        fmt = "%g";
-
-    } else if (n < 1e20) {
-        fmt = "%1.f";
-
-    } else {
-        fmt = "%1.e";
-    }
-
-    return snprintf((char *) buf, size, fmt, num);
-}
-
-
 njs_ret_t
 njs_number_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
diff -r 9322e8eab0b6 -r 0f9c060a0198 njs/njs_number.h
--- a/njs/njs_number.h	Wed Jul 18 15:41:55 2018 +0300
+++ b/njs/njs_number.h	Thu Jul 19 18:11:52 2018 +0300
@@ -20,7 +20,6 @@ int64_t njs_number_radix_parse(const u_c
     uint8_t radix);
 njs_ret_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
     const njs_value_t *number);
-size_t njs_num_to_buf(double num, u_char *buf, size_t size);
 njs_ret_t njs_number_constructor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 njs_ret_t njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args,
diff -r 9322e8eab0b6 -r 0f9c060a0198 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed Jul 18 15:41:55 2018 +0300
+++ b/njs/test/njs_unit_test.c	Thu Jul 19 18:11:52 2018 +0300
@@ -93,19 +93,44 @@ static njs_unit_test_t  njs_test[] =
 
     /* Numbers. */
 
+    { nxt_string("0"),
+      nxt_string("0") },
+
+    { nxt_string("-0"),
+      nxt_string("-0") },
+
+    { nxt_string("0.1"),
+      nxt_string("0.1") },
+
+    { nxt_string("0.000001"),
+      nxt_string("0.000001") },
+
+    { nxt_string("0.00000123456"),
+      nxt_string("0.00000123456") },
+
+    { nxt_string("0.0000001"),
+      nxt_string("1e-7") },
+
+    { nxt_string("1.1000000"),
+      nxt_string("1.1") },
+
+    { nxt_string("99999999999999999999"),
+      nxt_string("100000000000000000000") },
+
+    { nxt_string("99999999999999999999.111"),
+      nxt_string("100000000000000000000") },
+
     { nxt_string("999999999999999999999"),
       nxt_string("1e+21") },
 
-#if 0
     { nxt_string("9223372036854775808"),
-      nxt_string("9223372036854775808") },
+      nxt_string("9223372036854776000") },
 
     { nxt_string("18446744073709551616"),
       nxt_string("18446744073709552000") },
 
     { nxt_string("1.7976931348623157E+308"),
       nxt_string("1.7976931348623157e+308") },
-#endif
 
     { nxt_string("+1"),
       nxt_string("1") },
@@ -223,16 +248,16 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("57") },
 
     { nxt_string("5.7e-1"),
-      nxt_string("0.570000") },
+      nxt_string("0.57") },
 
     { nxt_string("-5.7e-1"),
-      nxt_string("-0.570000") },
+      nxt_string("-0.57") },
 
     { nxt_string("1.1e-01"),
-      nxt_string("0.110000") },
+      nxt_string("0.11") },
 
     { nxt_string("5.7e-2"),
-      nxt_string("0.057000") },
+      nxt_string("0.057") },
 
     { nxt_string("1.1e+01"),
       nxt_string("11") },
@@ -629,7 +654,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("NaN") },
 
     { nxt_string("var a = 0.1; a **= -2"),
-      nxt_string("100") },
+      nxt_string("99.99999999999999") },
 
     { nxt_string("var a = 1; a **= NaN"),
       nxt_string("NaN") },
@@ -6210,6 +6235,24 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Number('123')"),
       nxt_string("123") },
 
+    { nxt_string("Number('0.'+'1'.repeat(128))"),
+      nxt_string("0.1111111111111111") },
+
+    { nxt_string("Number('1'.repeat(128))"),
+      nxt_string("1.1111111111111113e+127") },
+
+    { nxt_string("Number('1'.repeat(129))"),
+      nxt_string("1.1111111111111112e+128") },
+
+    { nxt_string("Number('1'.repeat(129))"),
+      nxt_string("1.1111111111111112e+128") },
+
+    { nxt_string("Number('1'.repeat(129)+'e-100')"),
+      nxt_string("1.1111111111111112e+28") },
+
+    { nxt_string("Number('1'.repeat(310))"),
+      nxt_string("Infinity") },
+
     { nxt_string("var o = { valueOf: function() { return 123 } };"
                  "Number(o)"),
       nxt_string("123") },
@@ -7530,7 +7573,7 @@ static njs_unit_test_t  njs_test[] =
     /* Math. */
 
     { nxt_string("Math.PI"),
-      nxt_string("3.14159") },
+      nxt_string("3.141592653589793") },
 
     { nxt_string("Math.abs()"),
       nxt_string("NaN") },
@@ -7788,8 +7831,8 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Math.cbrt(-Infinity)"),
       nxt_string("-Infinity") },
 
-    { nxt_string("Math.cbrt('27')"),
-      nxt_string("3") },
+    { nxt_string("(Math.cbrt('27') - 3) < 1e-15"),
+      nxt_string("true") },
 
     { nxt_string("Math.cbrt(-1)"),
       nxt_string("-1") },
@@ -8570,10 +8613,10 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("57") },
 
     { nxt_string("parseFloat('-5.7e-1')"),
-      nxt_string("-0.570000") },
+      nxt_string("-0.57") },
 
     { nxt_string("parseFloat('-5.e-1')"),
-      nxt_string("-0.500000") },
+      nxt_string("-0.5") },
 
     { nxt_string("parseFloat('5.7e+01')"),
       nxt_string("57") },
@@ -8582,7 +8625,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("57") },
 
     { nxt_string("parseFloat('-5.7e-1abc')"),
-      nxt_string("-0.570000") },
+      nxt_string("-0.57") },
 
     { nxt_string("parseFloat('-5.7e')"),
       nxt_string("-5.7") },
@@ -8914,6 +8957,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("JSON.stringify(123)"),
       nxt_string("123") },
 
+    { nxt_string("JSON.stringify(0.00000123)"),
+      nxt_string("0.00000123") },
+
     { nxt_string("JSON.stringify(new Number(123))"),
       nxt_string("123") },
 
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/Makefile
--- a/nxt/Makefile	Wed Jul 18 15:41:55 2018 +0300
+++ b/nxt/Makefile	Thu Jul 19 18:11:52 2018 +0300
@@ -4,6 +4,9 @@ NXT_LIB =	nxt
 
 $(NXT_BUILDDIR)/libnxt.a: \
 	$(NXT_LIB)/nxt_auto_config.h \
+	$(NXT_BUILDDIR)/nxt_diyfp.o \
+	$(NXT_BUILDDIR)/nxt_dtoa.o \
+	$(NXT_BUILDDIR)/nxt_strtod.o \
 	$(NXT_BUILDDIR)/nxt_djb_hash.o \
 	$(NXT_BUILDDIR)/nxt_utf8.o \
 	$(NXT_BUILDDIR)/nxt_array.o \
@@ -20,6 +23,9 @@ NXT_LIB =	nxt
 	$(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
 
 	ar -r -c $(NXT_BUILDDIR)/libnxt.a \
+		$(NXT_BUILDDIR)/nxt_diyfp.o \
+		$(NXT_BUILDDIR)/nxt_dtoa.o \
+		$(NXT_BUILDDIR)/nxt_strtod.o \
 		$(NXT_BUILDDIR)/nxt_djb_hash.o \
 		$(NXT_BUILDDIR)/nxt_utf8.o \
 		$(NXT_BUILDDIR)/nxt_array.o \
@@ -34,6 +40,36 @@ NXT_LIB =	nxt
 		$(NXT_BUILDDIR)/nxt_trace.o \
 		$(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
 
+$(NXT_BUILDDIR)/nxt_diyfp.o: \
+	$(NXT_LIB)/nxt_types.h \
+	$(NXT_LIB)/nxt_clang.h \
+	$(NXT_LIB)/nxt_diyfp.h \
+	$(NXT_LIB)/nxt_diyfp.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_diyfp.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) \
+		$(NXT_LIB)/nxt_diyfp.c
+
+$(NXT_BUILDDIR)/nxt_dtoa.o: \
+	$(NXT_LIB)/nxt_types.h \
+	$(NXT_LIB)/nxt_clang.h \
+	$(NXT_LIB)/nxt_dtoa.h \
+	$(NXT_LIB)/nxt_dtoa.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_dtoa.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) \
+		$(NXT_LIB)/nxt_dtoa.c
+
+$(NXT_BUILDDIR)/nxt_strtod.o: \
+	$(NXT_LIB)/nxt_types.h \
+	$(NXT_LIB)/nxt_clang.h \
+	$(NXT_LIB)/nxt_strtod.h \
+	$(NXT_LIB)/nxt_strtod.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_strtod.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) \
+		$(NXT_LIB)/nxt_strtod.c
+
 $(NXT_BUILDDIR)/nxt_murmur_hash.o: \
 	$(NXT_LIB)/nxt_types.h \
 	$(NXT_LIB)/nxt_clang.h \
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/auto/clang
--- a/nxt/auto/clang	Wed Jul 18 15:41:55 2018 +0300
+++ b/nxt/auto/clang	Thu Jul 19 18:11:52 2018 +0300
@@ -166,6 +166,18 @@ END
 
 # C language features.
 
+nxt_feature="GCC unsigned __int128"
+nxt_feature_name=NXT_HAVE_UNSIGNED_INT128
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="int main(void) {
+                      unsigned __int128 p = 0;
+                      return (int) p;
+                  }"
+. ${NXT_AUTO}feature
+
+
 nxt_feature="GCC __builtin_expect()"
 nxt_feature_name=NXT_HAVE_BUILTIN_EXPECT
 nxt_feature_run=no
@@ -217,6 +229,20 @@ nxt_feature_test="int main(void) {
 . ${NXT_AUTO}feature
 
 
+nxt_feature="GCC __builtin_clzll()"
+nxt_feature_name=NXT_HAVE_BUILTIN_CLZLL
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="int main(void) {
+                      if (__builtin_clzll(1ULL) != 63) {
+                          return 1;
+                      }
+                      return 0;
+                  }"
+. ${NXT_AUTO}feature
+
+
 nxt_feature="GCC __attribute__ visibility"
 nxt_feature_name=NXT_HAVE_GCC_ATTRIBUTE_VISIBILITY
 nxt_feature_run=no
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/nxt_clang.h
--- a/nxt/nxt_clang.h	Wed Jul 18 15:41:55 2018 +0300
+++ b/nxt/nxt_clang.h	Thu Jul 19 18:11:52 2018 +0300
@@ -86,6 +86,38 @@ nxt_leading_zeros(uint32_t x)
 #endif
 
 
+#if (NXT_HAVE_BUILTIN_CLZLL)
+#define nxt_leading_zeros64(x)  (((x) == 0) ? 64 : __builtin_clzll(x))
+
+#else
+
+nxt_inline uint64_t
+nxt_leading_zeros64(uint64_t x)
+{
+    uint64_t  n;
+
+    /*
+     * There is no sense to optimize this function, since almost
+     * all platforms nowadays support the built-in instruction.
+     */
+
+    if (x == 0) {
+        return 64;
+    }
+
+    n = 0;
+
+    while ((x & 0x8000000000000000) == 0) {
+        n++;
+        x <<= 1;
+    }
+
+    return n;
+}
+
+#endif
+
+
 #if (NXT_HAVE_GCC_ATTRIBUTE_VISIBILITY)
 #define NXT_EXPORT         __attribute__((visibility("default")))
 
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/nxt_diyfp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nxt/nxt_diyfp.c	Thu Jul 19 18:11:52 2018 +0300
@@ -0,0 +1,150 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ *
+ * An internal diy_fp implementation.
+ * For details, see Loitsch, Florian. "Printing floating-point numbers quickly
+ * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_diyfp.h>
+
+
+typedef struct nxt_cpe_s {
+  uint64_t  significand;
+  int16_t   bin_exp;
+  int16_t   dec_exp;
+} nxt_cpe_t;
+
+
+static const nxt_cpe_t nxt_cached_powers[] = {
+  { nxt_uint64(0xfa8fd5a0, 0x081c0288), -1220, -348 },
+  { nxt_uint64(0xbaaee17f, 0xa23ebf76), -1193, -340 },
+  { nxt_uint64(0x8b16fb20, 0x3055ac76), -1166, -332 },
+  { nxt_uint64(0xcf42894a, 0x5dce35ea), -1140, -324 },
+  { nxt_uint64(0x9a6bb0aa, 0x55653b2d), -1113, -316 },
+  { nxt_uint64(0xe61acf03, 0x3d1a45df), -1087, -308 },
+  { nxt_uint64(0xab70fe17, 0xc79ac6ca), -1060, -300 },
+  { nxt_uint64(0xff77b1fc, 0xbebcdc4f), -1034, -292 },
+  { nxt_uint64(0xbe5691ef, 0x416bd60c), -1007, -284 },
+  { nxt_uint64(0x8dd01fad, 0x907ffc3c),  -980, -276 },
+  { nxt_uint64(0xd3515c28, 0x31559a83),  -954, -268 },
+  { nxt_uint64(0x9d71ac8f, 0xada6c9b5),  -927, -260 },
+  { nxt_uint64(0xea9c2277, 0x23ee8bcb),  -901, -252 },
+  { nxt_uint64(0xaecc4991, 0x4078536d),  -874, -244 },
+  { nxt_uint64(0x823c1279, 0x5db6ce57),  -847, -236 },
+  { nxt_uint64(0xc2109436, 0x4dfb5637),  -821, -228 },
+  { nxt_uint64(0x9096ea6f, 0x3848984f),  -794, -220 },
+  { nxt_uint64(0xd77485cb, 0x25823ac7),  -768, -212 },
+  { nxt_uint64(0xa086cfcd, 0x97bf97f4),  -741, -204 },
+  { nxt_uint64(0xef340a98, 0x172aace5),  -715, -196 },
+  { nxt_uint64(0xb23867fb, 0x2a35b28e),  -688, -188 },
+  { nxt_uint64(0x84c8d4df, 0xd2c63f3b),  -661, -180 },
+  { nxt_uint64(0xc5dd4427, 0x1ad3cdba),  -635, -172 },
+  { nxt_uint64(0x936b9fce, 0xbb25c996),  -608, -164 },
+  { nxt_uint64(0xdbac6c24, 0x7d62a584),  -582, -156 },
+  { nxt_uint64(0xa3ab6658, 0x0d5fdaf6),  -555, -148 },
+  { nxt_uint64(0xf3e2f893, 0xdec3f126),  -529, -140 },
+  { nxt_uint64(0xb5b5ada8, 0xaaff80b8),  -502, -132 },
+  { nxt_uint64(0x87625f05, 0x6c7c4a8b),  -475, -124 },
+  { nxt_uint64(0xc9bcff60, 0x34c13053),  -449, -116 },
+  { nxt_uint64(0x964e858c, 0x91ba2655),  -422, -108 },
+  { nxt_uint64(0xdff97724, 0x70297ebd),  -396, -100 },
+  { nxt_uint64(0xa6dfbd9f, 0xb8e5b88f),  -369,  -92 },
+  { nxt_uint64(0xf8a95fcf, 0x88747d94),  -343,  -84 },
+  { nxt_uint64(0xb9447093, 0x8fa89bcf),  -316,  -76 },
+  { nxt_uint64(0x8a08f0f8, 0xbf0f156b),  -289,  -68 },
+  { nxt_uint64(0xcdb02555, 0x653131b6),  -263,  -60 },
+  { nxt_uint64(0x993fe2c6, 0xd07b7fac),  -236,  -52 },
+  { nxt_uint64(0xe45c10c4, 0x2a2b3b06),  -210,  -44 },
+  { nxt_uint64(0xaa242499, 0x697392d3),  -183,  -36 },
+  { nxt_uint64(0xfd87b5f2, 0x8300ca0e),  -157,  -28 },
+  { nxt_uint64(0xbce50864, 0x92111aeb),  -130,  -20 },
+  { nxt_uint64(0x8cbccc09, 0x6f5088cc),  -103,  -12 },
+  { nxt_uint64(0xd1b71758, 0xe219652c),   -77,   -4 },
+  { nxt_uint64(0x9c400000, 0x00000000),   -50,    4 },
+  { nxt_uint64(0xe8d4a510, 0x00000000),   -24,   12 },
+  { nxt_uint64(0xad78ebc5, 0xac620000),     3,   20 },
+  { nxt_uint64(0x813f3978, 0xf8940984),    30,   28 },
+  { nxt_uint64(0xc097ce7b, 0xc90715b3),    56,   36 },
+  { nxt_uint64(0x8f7e32ce, 0x7bea5c70),    83,   44 },
+  { nxt_uint64(0xd5d238a4, 0xabe98068),   109,   52 },
+  { nxt_uint64(0x9f4f2726, 0x179a2245),   136,   60 },
+  { nxt_uint64(0xed63a231, 0xd4c4fb27),   162,   68 },
+  { nxt_uint64(0xb0de6538, 0x8cc8ada8),   189,   76 },
+  { nxt_uint64(0x83c7088e, 0x1aab65db),   216,   84 },
+  { nxt_uint64(0xc45d1df9, 0x42711d9a),   242,   92 },
+  { nxt_uint64(0x924d692c, 0xa61be758),   269,  100 },
+  { nxt_uint64(0xda01ee64, 0x1a708dea),   295,  108 },
+  { nxt_uint64(0xa26da399, 0x9aef774a),   322,  116 },
+  { nxt_uint64(0xf209787b, 0xb47d6b85),   348,  124 },
+  { nxt_uint64(0xb454e4a1, 0x79dd1877),   375,  132 },
+  { nxt_uint64(0x865b8692, 0x5b9bc5c2),   402,  140 },
+  { nxt_uint64(0xc83553c5, 0xc8965d3d),   428,  148 },
+  { nxt_uint64(0x952ab45c, 0xfa97a0b3),   455,  156 },
+  { nxt_uint64(0xde469fbd, 0x99a05fe3),   481,  164 },
+  { nxt_uint64(0xa59bc234, 0xdb398c25),   508,  172 },
+  { nxt_uint64(0xf6c69a72, 0xa3989f5c),   534,  180 },
+  { nxt_uint64(0xb7dcbf53, 0x54e9bece),   561,  188 },
+  { nxt_uint64(0x88fcf317, 0xf22241e2),   588,  196 },
+  { nxt_uint64(0xcc20ce9b, 0xd35c78a5),   614,  204 },
+  { nxt_uint64(0x98165af3, 0x7b2153df),   641,  212 },
+  { nxt_uint64(0xe2a0b5dc, 0x971f303a),   667,  220 },
+  { nxt_uint64(0xa8d9d153, 0x5ce3b396),   694,  228 },
+  { nxt_uint64(0xfb9b7cd9, 0xa4a7443c),   720,  236 },
+  { nxt_uint64(0xbb764c4c, 0xa7a44410),   747,  244 },
+  { nxt_uint64(0x8bab8eef, 0xb6409c1a),   774,  252 },
+  { nxt_uint64(0xd01fef10, 0xa657842c),   800,  260 },
+  { nxt_uint64(0x9b10a4e5, 0xe9913129),   827,  268 },
+  { nxt_uint64(0xe7109bfb, 0xa19c0c9d),   853,  276 },
+  { nxt_uint64(0xac2820d9, 0x623bf429),   880,  284 },
+  { nxt_uint64(0x80444b5e, 0x7aa7cf85),   907,  292 },
+  { nxt_uint64(0xbf21e440, 0x03acdd2d),   933,  300 },
+  { nxt_uint64(0x8e679c2f, 0x5e44ff8f),   960,  308 },
+  { nxt_uint64(0xd433179d, 0x9c8cb841),   986,  316 },
+  { nxt_uint64(0x9e19db92, 0xb4e31ba9),  1013,  324 },
+  { nxt_uint64(0xeb96bf6e, 0xbadf77d9),  1039,  332 },
+  { nxt_uint64(0xaf87023b, 0x9bf0ee6b),  1066,  340 },
+};
+
+
+#define NXT_D_1_LOG2_10     0.30102999566398114 /* 1 / log2(10). */
+
+
+nxt_diyfp_t
+nxt_cached_power_dec(int exp, int *dec_exp)
+{
+    u_int            index;
+    const nxt_cpe_t  *cp;
+
+    index = (exp + NXT_DECIMAL_EXPONENT_OFF) / NXT_DECIMAL_EXPONENT_DIST;
+    cp = &nxt_cached_powers[index];
+
+    *dec_exp = cp->dec_exp;
+
+    return nxt_diyfp(cp->significand, cp->bin_exp);
+}
+
+
+nxt_diyfp_t
+nxt_cached_power_bin(int exp, int *dec_exp)
+{
+    int              k;
+    u_int            index;
+    const nxt_cpe_t  *cp;
+
+    k = (int) ceil((-61 - exp) * NXT_D_1_LOG2_10)
+        + NXT_DECIMAL_EXPONENT_OFF - 1;
+
+    index = (unsigned) (k >> 3) + 1;
+
+    cp = &nxt_cached_powers[index];
+
+    *dec_exp = -(NXT_DECIMAL_EXPONENT_MIN + (int) (index << 3));
+
+    return nxt_diyfp(cp->significand, cp->bin_exp);
+}
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/nxt_diyfp.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nxt/nxt_diyfp.h	Thu Jul 19 18:11:52 2018 +0300
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ *
+ * An internal diy_fp implementation.
+ * For details, see Loitsch, Florian. "Printing floating-point numbers quickly
+ * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+ */
+
+#ifndef _NXT_DIYFP_H_INCLUDED_
+#define _NXT_DIYFP_H_INCLUDED_
+
+#include <nxt_types.h>
+#include <math.h>
+
+
+typedef struct {
+    uint64_t    significand;
+    int         exp;
+} nxt_diyfp_t;
+
+
+#define nxt_diyfp(_s, _e)           (nxt_diyfp_t) \
+                                    { .significand = (_s), .exp = (_e) }
+#define nxt_uint64(h, l)            (((uint64_t) (h) << 32) + (l))
+
+
+#define NXT_DBL_SIGNIFICAND_SIZE    52
+#define NXT_DBL_EXPONENT_BIAS       (0x3FF + NXT_DBL_SIGNIFICAND_SIZE)
+#define NXT_DBL_EXPONENT_MIN        (-NXT_DBL_EXPONENT_BIAS)
+#define NXT_DBL_EXPONENT_MAX        (0x7FF - NXT_DBL_EXPONENT_BIAS)
+#define NXT_DBL_EXPONENT_DENORMAL   (-NXT_DBL_EXPONENT_BIAS + 1)
+
+#define NXT_DBL_SIGNIFICAND_MASK    nxt_uint64(0x000FFFFF, 0xFFFFFFFF)
+#define NXT_DBL_HIDDEN_BIT          nxt_uint64(0x00100000, 0x00000000)
+#define NXT_DBL_EXPONENT_MASK       nxt_uint64(0x7FF00000, 0x00000000)
+
+#define NXT_DIYFP_SIGNIFICAND_SIZE  64
+
+#define NXT_SIGNIFICAND_SIZE        53
+#define NXT_SIGNIFICAND_SHIFT       (NXT_DIYFP_SIGNIFICAND_SIZE     \
+                                     - NXT_DBL_SIGNIFICAND_SIZE)
+
+#define NXT_DECIMAL_EXPONENT_OFF    348
+#define NXT_DECIMAL_EXPONENT_MIN    (-348)
+#define NXT_DECIMAL_EXPONENT_MAX    340
+#define NXT_DECIMAL_EXPONENT_DIST   8
+
+
+nxt_inline nxt_diyfp_t
+nxt_d2diyfp(double d)
+{
+    int           biased_exp;
+    uint64_t      significand;
+    nxt_diyfp_t   r;
+
+    union {
+        double    d;
+        uint64_t  u64;
+    } u;
+
+    u.d = d;
+
+    biased_exp = (u.u64 & NXT_DBL_EXPONENT_MASK) >> NXT_DBL_SIGNIFICAND_SIZE;
+    significand = u.u64 & NXT_DBL_SIGNIFICAND_MASK;
+
+    if (biased_exp != 0) {
+        r.significand = significand + NXT_DBL_HIDDEN_BIT;
+        r.exp = biased_exp - NXT_DBL_EXPONENT_BIAS;
+
+    } else {
+        r.significand = significand;
+        r.exp = NXT_DBL_EXPONENT_MIN + 1;
+    }
+
+    return r;
+}
+
+
+nxt_inline double
+nxt_diyfp2d(nxt_diyfp_t v)
+{
+    int           exp;
+    uint64_t      significand, biased_exp;
+
+    union {
+        double    d;
+        uint64_t  u64;
+    } u;
+
+    exp = v.exp;
+    significand = v.significand;
+
+    while (significand > NXT_DBL_HIDDEN_BIT + NXT_DBL_SIGNIFICAND_MASK) {
+        significand >>= 1;
+        exp++;
+    }
+
+    if (exp >= NXT_DBL_EXPONENT_MAX) {
+        return INFINITY;
+    }
+
+    if (exp < NXT_DBL_EXPONENT_DENORMAL) {
+        return 0.0;
+    }
+
+    while (exp > NXT_DBL_EXPONENT_DENORMAL
+           && (significand & NXT_DBL_HIDDEN_BIT) == 0)
+    {
+        significand <<= 1;
+        exp--;
+    }
+
+    if (exp == NXT_DBL_EXPONENT_DENORMAL
+        && (significand & NXT_DBL_HIDDEN_BIT) == 0)
+    {
+        biased_exp = 0;
+
+    } else {
+        biased_exp = (uint64_t) (exp + NXT_DBL_EXPONENT_BIAS);
+    }
+
+    u.u64 = (significand & NXT_DBL_SIGNIFICAND_MASK)
+            | (biased_exp << NXT_DBL_SIGNIFICAND_SIZE);
+
+    return u.d;
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_shift_left(nxt_diyfp_t v, unsigned shift)
+{
+    return nxt_diyfp(v.significand << shift, v.exp - shift);
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_shift_right(nxt_diyfp_t v, unsigned shift)
+{
+    return nxt_diyfp(v.significand >> shift, v.exp + shift);
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_sub(nxt_diyfp_t lhs, nxt_diyfp_t rhs)
+{
+    return nxt_diyfp(lhs.significand - rhs.significand, lhs.exp);
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_mul(nxt_diyfp_t lhs, nxt_diyfp_t rhs)
+{
+#if (NXT_HAVE_UNSIGNED_INT128)
+
+    uint64_t       l, h;
+    nxt_uint128_t  u128;
+
+    u128 = (nxt_uint128_t) (lhs.significand)
+           * (nxt_uint128_t) (rhs.significand);
+
+    h = u128 >> 64;
+    l = (uint64_t) u128;
+
+    /* rounding. */
+
+    if (l & ((uint64_t) 1 << 63)) {
+        h++;
+    }
+
+    return nxt_diyfp(h, lhs.exp + rhs.exp + 64);
+
+#else
+
+    uint64_t  a, b, c, d, ac, bc, ad, bd, tmp;
+
+    a = lhs.significand >> 32;
+    b = lhs.significand & 0xffffffff;
+    c = rhs.significand >> 32;
+    d = rhs.significand & 0xffffffff;
+
+    ac = a * c;
+    bc = b * c;
+    ad = a * d;
+    bd = b * d;
+
+    tmp = (bd >> 32) + (ad & 0xffffffff) + (bc & 0xffffffff);
+
+    /* mult_round. */
+
+    tmp += 1U << 31;
+
+    return nxt_diyfp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32),
+                     lhs.exp + rhs.exp + 64);
+
+#endif
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_normalize(nxt_diyfp_t v)
+{
+    return nxt_diyfp_shift_left(v, nxt_leading_zeros64(v.significand));
+}
+
+
+nxt_diyfp_t nxt_cached_power_dec(int exp, int *dec_exp);
+nxt_diyfp_t nxt_cached_power_bin(int exp, int *dec_exp);
+
+
+#endif /* _NXT_DIYFP_H_INCLUDED_ */
diff -r 9322e8eab0b6 -r 0f9c060a0198 nxt/nxt_dtoa.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nxt/nxt_dtoa.c	Thu Jul 19 18:11:52 2018 +0300
@@ -0,0 +1,363 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ *
+ * Grisu2 algorithm implementation for printing floating-point numbers based
+ * upon the work of Milo Yip and Doug Currie.
+ *
+ * For algorithm information, see Loitsch, Florian. "Printing
+ * floating-point numbers quickly and accurately with integers." ACM Sigplan
+ * Notices 45.6 (2010): 233-243.
+ *
+ * Copyright (C) 2015 Doug Currie
+ * based on dtoa_milo.h
+ * Copyright (C) 2014 Milo Yip
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_diyfp.h>
+#include <nxt_dtoa.h>
+
+#include <math.h>
+#include <string.h>
+
+
+nxt_inline void
+nxt_grisu2_round(char *start, size_t len, uint64_t delta, uint64_t rest,
+    uint64_t ten_kappa, uint64_t wp_w)
+{
+    while (rest < wp_w && delta - rest >= ten_kappa
+           && (rest + ten_kappa < wp_w ||  /* closer */
+               wp_w - rest > rest + ten_kappa - wp_w))
+    {
+        start[len - 1]--;
+        rest += ten_kappa;
+    }
+}
+
+
+nxt_inline int
+nxt_dec_count(uint32_t n)
+{
+    if (n < 10) return 1;
+    if (n < 100) return 2;
+    if (n < 1000) return 3;
+    if (n < 10000) return 4;
+    if (n < 100000) return 5;
+    if (n < 1000000) return 6;
+    if (n < 10000000) return 7;
+    if (n < 100000000) return 8;
+    if (n < 1000000000) return 9;
+
+    return 10;
+}
+
+
+nxt_inline size_t
+nxt_grisu2_gen(nxt_diyfp_t W, nxt_diyfp_t Mp, uint64_t delta, char *start,
+    int *dec_exp)
+{
+    int          kappa;
+    char         c, *p;
+    uint32_t     p1, d;
+    uint64_t     p2, tmp;
+    nxt_diyfp_t  one, wp_w;
+
+    static const uint64_t pow10[] = {
+        1,
+        10,
+        100,
+        1000,
+        10000,
+        100000,
+        1000000,
+        10000000,
+        100000000,
+        1000000000
+    };
+
+    wp_w = nxt_diyfp_sub(Mp, W);
+
+    one = nxt_diyfp((uint64_t) 1 << -Mp.exp, Mp.exp);
+    p1 = (uint32_t) (Mp.significand >> -one.exp);
+    p2 = Mp.significand & (one.significand - 1);
+
+    p = start;
+
+    kappa = nxt_dec_count(p1);
+
+    while (kappa > 0) {
+
+        switch (kappa) {
+            case 10: d = p1 / 1000000000; p1 %= 1000000000; break;
+            case  9: d = p1 /  100000000; p1 %=  100000000; break;
+            case  8: d = p1 /   10000000; p1 %=   10000000; break;
+            case  7: d = p1 /    1000000; p1 %=    1000000; break;
+            case  6: d = p1 /     100000; p1 %=     100000; break;
+            case  5: d = p1 /      10000; p1 %=      10000; break;
+            case  4: d = p1 /       1000; p1 %=       1000; break;
+            case  3: d = p1 /        100; p1 %=        100; break;
+            case  2: d = p1 /         10; p1 %=         10; break;
+            case  1: d = p1;              p1 =           0; break;
+            default:
+                nxt_unreachable();


More information about the nginx-devel mailing list