Adding correct dtoa() and strtod() realization.
This fixes #28 and #30 issues on GitHub.
$(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 \
$(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 \
#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>
return NXT_ERROR;
}
- size = njs_num_to_buf(num, p, 64);
+ size = nxt_dtoa(num, (char *) p);
njs_json_buf_written(stringify, size);
}
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);
}
}
} 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);
}
}
-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)
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,
/* 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") },
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") },
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") },
{ 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") },
/* Math. */
{ nxt_string("Math.PI"),
- nxt_string("3.14159") },
+ nxt_string("3.141592653589793") },
{ nxt_string("Math.abs()"),
nxt_string("NaN") },
{ 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") },
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") },
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") },
{ 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") },
$(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 \
$(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 \
$(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 \
# 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
. ${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
#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")))
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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_ */
--- /dev/null
+
+/*
+ * 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();
+ }
+
+ if (d != 0 || p != start) {
+ *p++ = '0' + d;
+ }
+
+ kappa--;
+
+ tmp = ((uint64_t) p1 << -one.exp) + p2;
+
+ if (tmp <= delta) {
+ *dec_exp += kappa;
+ nxt_grisu2_round(start, p - start, delta, tmp,
+ pow10[kappa] << -one.exp, wp_w.significand);
+ return p - start;
+ }
+ }
+
+ /* kappa = 0. */
+
+ for ( ;; ) {
+ p2 *= 10;
+ delta *= 10;
+ c = (char) (p2 >> -one.exp);
+
+ if (c != 0 || p != start) {
+ *p++ = '0' + c;
+ }
+
+ p2 &= one.significand - 1;
+ kappa--;
+
+ if (p2 < delta) {
+ *dec_exp += kappa;
+ tmp = (-kappa < 10) ? pow10[-kappa] : 0;
+ nxt_grisu2_round(start, p - start, delta, p2, one.significand,
+ wp_w.significand * tmp);
+ break;
+ }
+ }
+
+ return p - start;
+}
+
+
+nxt_inline nxt_diyfp_t
+nxt_diyfp_normalize_boundary(nxt_diyfp_t v)
+{
+ while ((v.significand & (NXT_DBL_HIDDEN_BIT << 1)) == 0) {
+ v.significand <<= 1;
+ v.exp--;
+ }
+
+ return nxt_diyfp_shift_left(v, NXT_SIGNIFICAND_SHIFT - 2);
+}
+
+
+nxt_inline void
+nxt_diyfp_normalize_boundaries(nxt_diyfp_t v, nxt_diyfp_t* minus,
+ nxt_diyfp_t* plus)
+{
+ nxt_diyfp_t pl, mi;
+
+ pl = nxt_diyfp_normalize_boundary(nxt_diyfp((v.significand << 1) + 1,
+ v.exp - 1));
+
+ if (v.significand == NXT_DBL_HIDDEN_BIT) {
+ mi = nxt_diyfp((v.significand << 2) - 1, v.exp - 2);
+
+ } else {
+ mi = nxt_diyfp((v.significand << 1) - 1, v.exp - 1);
+ }
+
+ mi.significand <<= mi.exp - pl.exp;
+ mi.exp = pl.exp;
+
+ *plus = pl;
+ *minus = mi;
+}
+
+
+nxt_inline size_t
+nxt_grisu2(double value, char *start, int *dec_exp)
+{
+ nxt_diyfp_t v, w_m, w_p, c_mk, W, Wp, Wm;
+
+ v = nxt_d2diyfp(value);
+
+ nxt_diyfp_normalize_boundaries(v, &w_m, &w_p);
+
+ c_mk = nxt_cached_power_bin(w_p.exp, dec_exp);
+ W = nxt_diyfp_mul(nxt_diyfp_normalize(v), c_mk);
+
+ Wp = nxt_diyfp_mul(w_p, c_mk);
+ Wm = nxt_diyfp_mul(w_m, c_mk);
+
+ Wm.significand++;
+ Wp.significand--;
+
+ return nxt_grisu2_gen(W, Wp, Wp.significand - Wm.significand, start,
+ dec_exp);
+}
+
+
+nxt_inline size_t
+nxt_write_exponent(int exp, char* start)
+{
+ char *p;
+ size_t len;
+ uint32_t u32;
+ char buf[4];
+
+ /* -324 <= exp <= 308. */
+
+ if (exp < 0) {
+ *start++ = '-';
+ exp = -exp;
+
+ } else {
+ *start++ = '+';
+ }
+
+ u32 = exp;
+ p = buf + nxt_length(buf);
+
+ do {
+ *--p = u32 % 10 + '0';
+ u32 /= 10;
+ } while (u32 != 0);
+
+ len = buf + nxt_length(buf) - p;
+
+ memcpy(start, p, len);
+
+ return len + 1;
+}
+
+
+nxt_inline size_t
+nxt_prettify(char *start, size_t len, int dec_exp)
+{
+ int kk, offset, length;
+ size_t size;
+
+ /* 10^(kk-1) <= v < 10^kk */
+
+ length = (int) len;
+ kk = length + dec_exp;
+
+ if (length <= kk && kk <= 21) {
+
+ /* 1234e7 -> 12340000000 */
+
+ if (kk - length > 0) {
+ memset(&start[length], '0', kk - length);
+ }
+
+ return kk;
+
+ } else if (0 < kk && kk <= 21) {
+
+ /* 1234e-2 -> 12.34 */
+
+ memmove(&start[kk + 1], &start[kk], length - kk);
+ start[kk] = '.';
+
+ return (length + 1);
+
+ } else if (-6 < kk && kk <= 0) {
+
+ /* 1234e-6 -> 0.001234 */
+
+ offset = 2 - kk;
+ memmove(&start[offset], start, length);
+
+ start[0] = '0';
+ start[1] = '.';
+
+ if (offset - 2 > 0) {
+ memset(&start[2], '0', offset - 2);
+ }
+
+ return (length + offset);
+
+ } else if (length == 1) {
+
+ /* 1e30 */
+
+ start[1] = 'e';
+
+ size = nxt_write_exponent(kk - 1, &start[2]);
+
+ return (size + 2);
+
+ }
+
+ /* 1234e30 -> 1.234e33 */
+
+ memmove(&start[2], &start[1], length - 1);
+ start[1] = '.';
+ start[length + 1] = 'e';
+
+ size = nxt_write_exponent(kk - 1, &start[length + 2]);
+
+ return (size + length + 2);
+}
+
+
+size_t
+nxt_dtoa(double value, char *start)
+{
+ int dec_exp, minus;
+ char *p;
+ size_t length;
+
+ /* Not handling NaN and inf. */
+
+ minus = 0;
+ p = start;
+
+ if (signbit(value)) {
+ *p++ = '-';
+ value = -value;
+ minus = 1;
+ }
+
+ if (value == 0) {
+ *p++ = '0';
+
+ return (p - start);
+ }
+
+ length = nxt_grisu2(value, p, &dec_exp);
+
+ length = nxt_prettify(p, length, dec_exp);
+
+ return (minus + length);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#ifndef _NXT_DTOA_H_INCLUDED_
+#define _NXT_DTOA_H_INCLUDED_
+
+NXT_EXPORT size_t nxt_dtoa(double value, char* buffer);
+
+#endif /* _NXT_DTOA_H_INCLUDED_ */
--- /dev/null
+/*
+ * An internal strtod() implementation based upon V8 src/strtod.cc
+ * without bignum support.
+ *
+ * Copyright 2012 the V8 project authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_diyfp.h>
+#include <nxt_strtod.h>
+
+/*
+ * Max double: 1.7976931348623157 x 10^308
+ * Min non-zero double: 4.9406564584124654 x 10^-324
+ * Any x >= 10^309 is interpreted as +infinity.
+ * Any x <= 10^-324 is interpreted as 0.
+ * Note that 2.5e-324 (despite being smaller than the min double)
+ * will be read as non-zero (equal to the min non-zero double).
+ */
+
+#define NXT_DECIMAL_POWER_MAX 309
+#define NXT_DECIMAL_POWER_MIN (-324)
+
+#define NXT_UINT64_MAX nxt_uint64(0xFFFFFFFF, 0xFFFFFFFF)
+#define NXT_UINT64_DECIMAL_DIGITS_MAX 19
+
+
+/*
+ * Reads digits from the buffer and converts them to a uint64.
+ * Reads in as many digits as fit into a uint64.
+ * When the string starts with "1844674407370955161" no further digit is read.
+ * Since 2^64 = 18446744073709551616 it would still be possible read another
+ * digit if it was less or equal than 6, but this would complicate the code.
+ */
+nxt_inline uint64_t
+nxt_read_uint64(const u_char *start, size_t length, size_t *ndigits)
+{
+ u_char d;
+ uint64_t value;
+ const u_char *p, *e;
+
+ value = 0;
+
+ p = start;
+ e = p + length;
+
+ while (p < e && value <= (NXT_UINT64_MAX / 10 - 1)) {
+ d = *p++ - '0';
+ value = 10 * value + d;
+ }
+
+ *ndigits = p - start;
+
+ return value;
+}
+
+
+/*
+ * Reads a nxt_diyfp_t from the buffer.
+ * The returned nxt_diyfp_t is not necessarily normalized.
+ * If remaining is zero then the returned nxt_diyfp_t is accurate.
+ * Otherwise it has been rounded and has error of at most 1/2 ulp.
+ */
+static nxt_diyfp_t
+nxt_diyfp_read(const u_char *start, size_t length, int *remaining)
+{
+ size_t read;
+ uint64_t significand;
+
+ significand = nxt_read_uint64(start, length, &read);
+
+ /* Round the significand. */
+
+ if (length != read) {
+ if (start[read] >= '5') {
+ significand++;
+ }
+ }
+
+ *remaining = length - read;
+
+ return nxt_diyfp(significand, 0);
+}
+
+
+/*
+ * Returns 10^exp as an exact nxt_diyfp_t.
+ * The given exp must be in the range [1; NXT_DECIMAL_EXPONENT_DIST[.
+ */
+nxt_inline nxt_diyfp_t
+nxt_adjust_pow10(int exp)
+{
+ switch (exp) {
+ case 1:
+ return nxt_diyfp(nxt_uint64(0xa0000000, 00000000), -60);
+ case 2:
+ return nxt_diyfp(nxt_uint64(0xc8000000, 00000000), -57);
+ case 3:
+ return nxt_diyfp(nxt_uint64(0xfa000000, 00000000), -54);
+ case 4:
+ return nxt_diyfp(nxt_uint64(0x9c400000, 00000000), -50);
+ case 5:
+ return nxt_diyfp(nxt_uint64(0xc3500000, 00000000), -47);
+ case 6:
+ return nxt_diyfp(nxt_uint64(0xf4240000, 00000000), -44);
+ case 7:
+ return nxt_diyfp(nxt_uint64(0x98968000, 00000000), -40);
+ default:
+ nxt_unreachable();
+ }
+}
+
+
+/*
+ * Returns the significand size for a given order of magnitude.
+ * If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
+ * This function returns the number of significant binary digits v will have
+ * once its encoded into a double. In almost all cases this is equal to
+ * NXT_SIGNIFICAND_SIZE. The only exception are denormals. They start with
+ * leading zeroes and their effective significand-size is hence smaller.
+ */
+nxt_inline int
+nxt_diyfp_sgnd_size(int order)
+{
+ if (order >= (NXT_DBL_EXPONENT_DENORMAL + NXT_SIGNIFICAND_SIZE)) {
+ return NXT_SIGNIFICAND_SIZE;
+ }
+
+ if (order <= NXT_DBL_EXPONENT_DENORMAL) {
+ return 0;
+ }
+
+ return order - NXT_DBL_EXPONENT_DENORMAL;
+}
+
+
+#define NXT_DENOM_LOG 3
+#define NXT_DENOM (1 << NXT_DENOM_LOG)
+
+/*
+ * Returns either the correct double or the double that is just below
+ * the correct double.
+ */
+static double
+nxt_diyfp_strtod(const u_char *start, size_t length, int exp)
+{
+ int magnitude, prec_digits;
+ int remaining, dec_exp, adj_exp, orig_e, shift;
+ int64_t error;
+ uint64_t prec_bits, half_way;
+ nxt_diyfp_t value, pow, adj_pow, rounded;
+
+ value = nxt_diyfp_read(start, length, &remaining);
+
+ exp += remaining;
+
+ /*
+ * Since some digits may have been dropped the value is not accurate.
+ * If remaining is different than 0 than the error is at most .5 ulp
+ * (unit in the last place).
+ * Using a common denominator to avoid dealing with fractions.
+ */
+
+ error = (remaining == 0 ? 0 : NXT_DENOM / 2);
+
+ orig_e = value.exp;
+ value = nxt_diyfp_normalize(value);
+ error <<= orig_e - value.exp;
+
+ if (exp < NXT_DECIMAL_EXPONENT_MIN) {
+ return 0.0;
+ }
+
+ pow = nxt_cached_power_dec(exp, &dec_exp);
+
+ if (dec_exp != exp) {
+
+ adj_exp = exp - dec_exp;
+ adj_pow = nxt_adjust_pow10(exp - dec_exp);
+ value = nxt_diyfp_mul(value, adj_pow);
+
+ if (NXT_UINT64_DECIMAL_DIGITS_MAX - (int) length < adj_exp) {
+ /*
+ * The adjustment power is exact. There is hence only
+ * an error of 0.5.
+ */
+ error += NXT_DENOM / 2;
+ }
+ }
+
+ value = nxt_diyfp_mul(value, pow);
+
+ /*
+ * The error introduced by a multiplication of a * b equals
+ * error_a + error_b + error_a * error_b / 2^64 + 0.5
+ * Substituting a with 'value' and b with 'pow':
+ * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
+ * error_ab = 0 or 1 / NXT_DENOM > error_a * error_b / 2^64.
+ */
+
+ error += NXT_DENOM + (error != 0 ? 1 : 0);
+
+ orig_e = value.exp;
+ value = nxt_diyfp_normalize(value);
+ error <<= orig_e - value.exp;
+
+ /*
+ * Check whether the double's significand changes when the error is added
+ * or substracted.
+ */
+
+ magnitude = NXT_DIYFP_SIGNIFICAND_SIZE + value.exp;
+ prec_digits = NXT_DIYFP_SIGNIFICAND_SIZE - nxt_diyfp_sgnd_size(magnitude);
+
+ if (prec_digits + NXT_DENOM_LOG >= NXT_DIYFP_SIGNIFICAND_SIZE) {
+ /*
+ * This can only happen for very small denormals. In this case the
+ * half-way multiplied by the denominator exceeds the range of uint64.
+ * Simply shift everything to the right.
+ */
+ shift = prec_digits + NXT_DENOM_LOG - NXT_DIYFP_SIGNIFICAND_SIZE + 1;
+
+ value = nxt_diyfp_shift_right(value, shift);
+
+ /*
+ * Add 1 for the lost precision of error, and NXT_DENOM
+ * for the lost precision of value.significand.
+ */
+ error = (error >> shift) + 1 + NXT_DENOM;
+ prec_digits -= shift;
+ }
+
+ prec_bits = value.significand & (((uint64_t) 1 << prec_digits) - 1);
+ prec_bits *= NXT_DENOM;
+
+ half_way = (uint64_t) 1 << (prec_digits - 1);
+ half_way *= NXT_DENOM;
+
+ rounded = nxt_diyfp_shift_right(value, prec_digits);
+
+ if (prec_bits >= half_way + error) {
+ rounded.significand++;
+ }
+
+ return nxt_diyfp2d(rounded);
+}
+
+
+static double
+nxt_strtod_internal(const u_char *start, size_t length, int exp)
+{
+ size_t left, right;
+ const u_char *p, *e, *b;
+
+ /* Trim leading zeroes. */
+
+ p = start;
+ e = p + length;
+
+ while (p < e) {
+ if (*p != '0') {
+ start = p;
+ break;
+ }
+
+ p++;
+ }
+
+ left = e - p;
+
+ /* Trim trailing zeroes. */
+
+ b = start;
+ p = b + left - 1;
+
+ while (p > b) {
+ if (*p != '0') {
+ break;
+ }
+
+ p--;
+ }
+
+ right = p - b + 1;
+
+ length = right;
+
+ if (length == 0) {
+ return 0.0;
+ }
+
+ exp += (int) (left - right);
+
+ if (exp + (int) length - 1 >= NXT_DECIMAL_POWER_MAX) {
+ return INFINITY;
+ }
+
+ if (exp + (int) length <= NXT_DECIMAL_POWER_MIN) {
+ return 0.0;
+ }
+
+ return nxt_diyfp_strtod(start, length, exp);
+}
+
+
+double
+nxt_strtod(const u_char **start, const u_char *end)
+{
+ int exponent, exp, insignf;
+ u_char c, *pos;
+ nxt_bool_t minus;
+ const u_char *e, *p, *last;
+ u_char data[128];
+
+ exponent = 0;
+ insignf = 0;
+
+ pos = data;
+ last = data + sizeof(data);
+
+ for (p = *start; p < end; p++) {
+ /* Values less than '0' become >= 208. */
+ c = *p - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ if (pos < last) {
+ *pos++ = *p;
+
+ } else {
+ insignf++;
+ }
+ }
+
+ /* Do not emit a '.', but adjust the exponent instead. */
+ if (p < end && *p == '.') {
+
+ for (p++; p < end; p++) {
+ /* Values less than '0' become >= 208. */
+ c = *p - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ if (pos < last) {
+ *pos++ = *p;
+ exponent--;
+
+ } else {
+ /* Ignore insignificant digits in the fractional part. */
+ }
+ }
+ }
+
+ 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)) {
+ exp = c;
+
+ for (p = e + 1; p < end; p++) {
+ /* Values less than '0' become >= 208. */
+ c = *p - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ exp = exp * 10 + c;
+ }
+
+ exponent += minus ? -exp : exp;
+ }
+ }
+
+ *start = p;
+
+ exponent += insignf;
+
+ return nxt_strtod_internal(data, pos - data, exponent);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#ifndef _NXT_STRTOD_H_INCLUDED_
+#define _NXT_STRTOD_H_INCLUDED_
+
+NXT_EXPORT double nxt_strtod(const u_char **start, const u_char *end);
+
+#endif /* _NXT_STRTOD_H_INCLUDED_ */
#endif
+#if (NXT_HAVE_UNSIGNED_INT128)
+typedef unsigned __int128 nxt_uint128_t;
+#endif
+
+
typedef nxt_uint_t nxt_bool_t;