# C language features.
-njs_feature="GCC unsigned __int128"
-njs_feature_name=NJS_HAVE_UNSIGNED_INT128
-njs_feature_run=no
-njs_feature_incs=
-njs_feature_libs=
-njs_feature_test="int main(void) {
- unsigned __int128 p = 0;
- return (int) p;
- }"
-. auto/feature
-
-
njs_feature="GCC __builtin_expect()"
njs_feature_name=NJS_HAVE_BUILTIN_EXPECT
njs_feature_run=no
return 0;
}"
. auto/feature
-
-
-njs_feature="_mm_setcsr()"
-njs_feature_name=NJS_HAVE_DENORMALS_CONTROL
-njs_feature_run=no
-njs_feature_incs=
-njs_feature_libs=
-njs_feature_test="#include <xmmintrin.h>
- int main(void) {
- _mm_setcsr(_mm_getcsr());
- return 0;
- }"
-. auto/feature
NJS_LIB_SRCS=" \
- src/njs_diyfp.c \
src/njs_dtoa.c \
- src/njs_dtoa_fixed.c \
src/njs_str.c \
- src/njs_strtod.c \
src/njs_murmur_hash.c \
src/njs_djb_hash.c \
src/njs_utf8.c \
return 0;
}"
. auto/feature
-
-
-# Ensuring that double type is always evaluated at standard
-# precision required by njs_diyfp_t
-
-
-case $NJS_CC_NAME in
-
- gcc)
- NJS_CFLAGS="$NJS_CFLAGS -fexcess-precision=standard"
- ;;
-
- clang)
-
- njs_found=no
-
- njs_feature="flag -ffp-eval-method=double"
- njs_feature_name=NJS_HAVE_FP_EVAL_METHOD
- njs_feature_run=no
- njs_feature_incs="-ffp-eval-method=double"
- njs_feature_libs=
- njs_feature_test="int main(void) {
- return 0;
- }"
-
- . auto/feature
-
- if [ $njs_found = yes ]; then
- NJS_CFLAGS="$NJS_CFLAGS -ffp-eval-method=double"
- fi
-
- ;;
-
- SunC)
-
- njs_found=no
-
- njs_feature="flag -xarch=sse2"
- njs_feature_name=NJS_HAVE_XARCH_SSE2
- njs_feature_run=no
- njs_feature_incs="-xarch=sse2"
- njs_feature_libs=
- njs_feature_test="int main(void) {
- return 0;
- }"
-
- . auto/feature
-
- if [ $njs_found = yes ]; then
- NJS_CFLAGS="$NJS_CFLAGS -xarch=sse2"
- fi
- ;;
-
-esac
njs_int_t ret;
njs_engine_t *engine;
- njs_mm_denormals(opts->denormals);
-
if (opts->file == NULL) {
if (opts->command.length != 0) {
opts->file = (char *) "string";
return NJS_ERROR;
case 'f':
-
-#if !(NJS_HAVE_DENORMALS_CONTROL)
- njs_stderror("option \"-f\" is not supported\n");
- return NJS_ERROR;
-#endif
-
opts->denormals = 0;
break;
NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm);
NJS_EXPORT njs_int_t njs_vm_call_exit_hook(njs_vm_t *vm);
+/* Input must be null-terminated. */
NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
NJS_EXPORT void njs_vm_set_module_loader(njs_vm_t *vm,
njs_module_loader_t module_loader, void *opaque);
NJS_EXPORT njs_mod_t *njs_vm_add_module(njs_vm_t *vm, njs_str_t *name,
njs_value_t *value);
+/* Input must be null-terminated. */
NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name,
u_char **start, u_char *end);
NJS_EXPORT njs_int_t njs_vm_reuse(njs_vm_t *vm);
}
-#if (NJS_HAVE_DENORMALS_CONTROL)
-#include <xmmintrin.h>
-
-/*
- * 0x8000 Flush to zero
- * 0x0040 Denormals are zeros
- */
-
-#define NJS_MM_DENORMALS_MASK 0x8040
-
-#define njs_mm_denormals(on) \
- _mm_setcsr((_mm_getcsr() & ~NJS_MM_DENORMALS_MASK) | (!(on) ? 0x8040: 0x0))
-#else
-
-#define njs_mm_denormals(on)
-#endif
-
-
#ifndef NJS_MAX_ALIGNMENT
#if (NJS_SOLARIS)
--- /dev/null
+/*
+ * Selected C utilities adapted from QuickJS cutils.h
+ *
+ * Copyright (c) 2024 Fabrice Bellard
+ */
+
+#ifndef _NJS_CUTILS_H_INCLUDED_
+#define _NJS_CUTILS_H_INCLUDED_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef likely
+#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+#ifndef no_inline
+#define no_inline __attribute__((noinline))
+#endif
+
+/* compatibility attribute used in dtoa implementation */
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+
+typedef int BOOL;
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+static inline int
+max_int(int a, int b)
+{
+ return (a > b) ? a : b;
+}
+
+static inline int
+min_int(int a, int b)
+{
+ return (a < b) ? a : b;
+}
+
+static inline uint32_t
+max_uint32(uint32_t a, uint32_t b)
+{
+ return (a > b) ? a : b;
+}
+
+static inline uint32_t
+min_uint32(uint32_t a, uint32_t b)
+{
+ return (a < b) ? a : b;
+}
+
+static inline int64_t
+max_int64(int64_t a, int64_t b)
+{
+ return (a > b) ? a : b;
+}
+
+static inline int64_t
+min_int64(int64_t a, int64_t b)
+{
+ return (a < b) ? a : b;
+}
+
+/* WARNING: undefined if a = 0 */
+static inline int
+clz32(unsigned int a)
+{
+ return __builtin_clz(a);
+}
+
+/* WARNING: undefined if a = 0 */
+static inline int
+clz64(uint64_t a)
+{
+ return __builtin_clzll(a);
+}
+
+/* WARNING: undefined if a = 0 */
+static inline int
+ctz32(unsigned int a)
+{
+ return __builtin_ctz(a);
+}
+
+/* WARNING: undefined if a = 0 */
+static inline int
+ctz64(uint64_t a)
+{
+ return __builtin_ctzll(a);
+}
+
+static inline uint64_t
+float64_as_uint64(double d)
+{
+ union {
+ double d;
+ uint64_t u64;
+ } u;
+
+ u.d = d;
+ return u.u64;
+}
+
+static inline double
+uint64_as_float64(uint64_t u64)
+{
+ union {
+ double d;
+ uint64_t u64;
+ } u;
+
+ u.u64 = u64;
+ return u.d;
+}
+
+static inline int
+strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p = str;
+ const char *q = val;
+
+ while (*q != '\0') {
+ if (*p != *q) {
+ return 0;
+ }
+ p++;
+ q++;
+ }
+
+ if (ptr != NULL) {
+ *ptr = p;
+ }
+
+ return 1;
+}
+
+#endif /* _NJS_CUTILS_H_INCLUDED_ */
+++ /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 <njs_main.h>
-#include <njs_diyfp.h>
-
-
-typedef struct njs_cpe_s {
- uint64_t significand;
- int16_t bin_exp;
- int16_t dec_exp;
-} njs_cpe_t;
-
-
-static const njs_cpe_t njs_cached_powers[] = {
- { njs_uint64(0xfa8fd5a0, 0x081c0288), -1220, -348 },
- { njs_uint64(0xbaaee17f, 0xa23ebf76), -1193, -340 },
- { njs_uint64(0x8b16fb20, 0x3055ac76), -1166, -332 },
- { njs_uint64(0xcf42894a, 0x5dce35ea), -1140, -324 },
- { njs_uint64(0x9a6bb0aa, 0x55653b2d), -1113, -316 },
- { njs_uint64(0xe61acf03, 0x3d1a45df), -1087, -308 },
- { njs_uint64(0xab70fe17, 0xc79ac6ca), -1060, -300 },
- { njs_uint64(0xff77b1fc, 0xbebcdc4f), -1034, -292 },
- { njs_uint64(0xbe5691ef, 0x416bd60c), -1007, -284 },
- { njs_uint64(0x8dd01fad, 0x907ffc3c), -980, -276 },
- { njs_uint64(0xd3515c28, 0x31559a83), -954, -268 },
- { njs_uint64(0x9d71ac8f, 0xada6c9b5), -927, -260 },
- { njs_uint64(0xea9c2277, 0x23ee8bcb), -901, -252 },
- { njs_uint64(0xaecc4991, 0x4078536d), -874, -244 },
- { njs_uint64(0x823c1279, 0x5db6ce57), -847, -236 },
- { njs_uint64(0xc2109436, 0x4dfb5637), -821, -228 },
- { njs_uint64(0x9096ea6f, 0x3848984f), -794, -220 },
- { njs_uint64(0xd77485cb, 0x25823ac7), -768, -212 },
- { njs_uint64(0xa086cfcd, 0x97bf97f4), -741, -204 },
- { njs_uint64(0xef340a98, 0x172aace5), -715, -196 },
- { njs_uint64(0xb23867fb, 0x2a35b28e), -688, -188 },
- { njs_uint64(0x84c8d4df, 0xd2c63f3b), -661, -180 },
- { njs_uint64(0xc5dd4427, 0x1ad3cdba), -635, -172 },
- { njs_uint64(0x936b9fce, 0xbb25c996), -608, -164 },
- { njs_uint64(0xdbac6c24, 0x7d62a584), -582, -156 },
- { njs_uint64(0xa3ab6658, 0x0d5fdaf6), -555, -148 },
- { njs_uint64(0xf3e2f893, 0xdec3f126), -529, -140 },
- { njs_uint64(0xb5b5ada8, 0xaaff80b8), -502, -132 },
- { njs_uint64(0x87625f05, 0x6c7c4a8b), -475, -124 },
- { njs_uint64(0xc9bcff60, 0x34c13053), -449, -116 },
- { njs_uint64(0x964e858c, 0x91ba2655), -422, -108 },
- { njs_uint64(0xdff97724, 0x70297ebd), -396, -100 },
- { njs_uint64(0xa6dfbd9f, 0xb8e5b88f), -369, -92 },
- { njs_uint64(0xf8a95fcf, 0x88747d94), -343, -84 },
- { njs_uint64(0xb9447093, 0x8fa89bcf), -316, -76 },
- { njs_uint64(0x8a08f0f8, 0xbf0f156b), -289, -68 },
- { njs_uint64(0xcdb02555, 0x653131b6), -263, -60 },
- { njs_uint64(0x993fe2c6, 0xd07b7fac), -236, -52 },
- { njs_uint64(0xe45c10c4, 0x2a2b3b06), -210, -44 },
- { njs_uint64(0xaa242499, 0x697392d3), -183, -36 },
- { njs_uint64(0xfd87b5f2, 0x8300ca0e), -157, -28 },
- { njs_uint64(0xbce50864, 0x92111aeb), -130, -20 },
- { njs_uint64(0x8cbccc09, 0x6f5088cc), -103, -12 },
- { njs_uint64(0xd1b71758, 0xe219652c), -77, -4 },
- { njs_uint64(0x9c400000, 0x00000000), -50, 4 },
- { njs_uint64(0xe8d4a510, 0x00000000), -24, 12 },
- { njs_uint64(0xad78ebc5, 0xac620000), 3, 20 },
- { njs_uint64(0x813f3978, 0xf8940984), 30, 28 },
- { njs_uint64(0xc097ce7b, 0xc90715b3), 56, 36 },
- { njs_uint64(0x8f7e32ce, 0x7bea5c70), 83, 44 },
- { njs_uint64(0xd5d238a4, 0xabe98068), 109, 52 },
- { njs_uint64(0x9f4f2726, 0x179a2245), 136, 60 },
- { njs_uint64(0xed63a231, 0xd4c4fb27), 162, 68 },
- { njs_uint64(0xb0de6538, 0x8cc8ada8), 189, 76 },
- { njs_uint64(0x83c7088e, 0x1aab65db), 216, 84 },
- { njs_uint64(0xc45d1df9, 0x42711d9a), 242, 92 },
- { njs_uint64(0x924d692c, 0xa61be758), 269, 100 },
- { njs_uint64(0xda01ee64, 0x1a708dea), 295, 108 },
- { njs_uint64(0xa26da399, 0x9aef774a), 322, 116 },
- { njs_uint64(0xf209787b, 0xb47d6b85), 348, 124 },
- { njs_uint64(0xb454e4a1, 0x79dd1877), 375, 132 },
- { njs_uint64(0x865b8692, 0x5b9bc5c2), 402, 140 },
- { njs_uint64(0xc83553c5, 0xc8965d3d), 428, 148 },
- { njs_uint64(0x952ab45c, 0xfa97a0b3), 455, 156 },
- { njs_uint64(0xde469fbd, 0x99a05fe3), 481, 164 },
- { njs_uint64(0xa59bc234, 0xdb398c25), 508, 172 },
- { njs_uint64(0xf6c69a72, 0xa3989f5c), 534, 180 },
- { njs_uint64(0xb7dcbf53, 0x54e9bece), 561, 188 },
- { njs_uint64(0x88fcf317, 0xf22241e2), 588, 196 },
- { njs_uint64(0xcc20ce9b, 0xd35c78a5), 614, 204 },
- { njs_uint64(0x98165af3, 0x7b2153df), 641, 212 },
- { njs_uint64(0xe2a0b5dc, 0x971f303a), 667, 220 },
- { njs_uint64(0xa8d9d153, 0x5ce3b396), 694, 228 },
- { njs_uint64(0xfb9b7cd9, 0xa4a7443c), 720, 236 },
- { njs_uint64(0xbb764c4c, 0xa7a44410), 747, 244 },
- { njs_uint64(0x8bab8eef, 0xb6409c1a), 774, 252 },
- { njs_uint64(0xd01fef10, 0xa657842c), 800, 260 },
- { njs_uint64(0x9b10a4e5, 0xe9913129), 827, 268 },
- { njs_uint64(0xe7109bfb, 0xa19c0c9d), 853, 276 },
- { njs_uint64(0xac2820d9, 0x623bf429), 880, 284 },
- { njs_uint64(0x80444b5e, 0x7aa7cf85), 907, 292 },
- { njs_uint64(0xbf21e440, 0x03acdd2d), 933, 300 },
- { njs_uint64(0x8e679c2f, 0x5e44ff8f), 960, 308 },
- { njs_uint64(0xd433179d, 0x9c8cb841), 986, 316 },
- { njs_uint64(0x9e19db92, 0xb4e31ba9), 1013, 324 },
- { njs_uint64(0xeb96bf6e, 0xbadf77d9), 1039, 332 },
- { njs_uint64(0xaf87023b, 0x9bf0ee6b), 1066, 340 },
-};
-
-
-#define NJS_D_1_LOG2_10 0.30102999566398114 /* 1 / log2(10). */
-
-
-njs_diyfp_t
-njs_cached_power_dec(int exp, int *dec_exp)
-{
- u_int index;
- const njs_cpe_t *cp;
-
- index = (exp + NJS_DECIMAL_EXPONENT_OFF) / NJS_DECIMAL_EXPONENT_DIST;
- cp = &njs_cached_powers[index];
-
- *dec_exp = cp->dec_exp;
-
- return njs_diyfp(cp->significand, cp->bin_exp);
-}
-
-
-njs_diyfp_t
-njs_cached_power_bin(int exp, int *dec_exp)
-{
- int k;
- u_int index;
- const njs_cpe_t *cp;
-
- k = (int) ceil((-61 - exp) * NJS_D_1_LOG2_10)
- + NJS_DECIMAL_EXPONENT_OFF - 1;
-
- index = (unsigned) (k >> 3) + 1;
-
- cp = &njs_cached_powers[index];
-
- *dec_exp = -(NJS_DECIMAL_EXPONENT_MIN + (int) (index << 3));
-
- return njs_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 _NJS_DIYFP_H_INCLUDED_
-#define _NJS_DIYFP_H_INCLUDED_
-
-
-typedef struct {
- uint64_t significand;
- int exp;
-} 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_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_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 \
- - NJS_DBL_SIGNIFICAND_SIZE)
-
-#define NJS_DECIMAL_EXPONENT_OFF 348
-#define NJS_DECIMAL_EXPONENT_MIN (-348)
-#define NJS_DECIMAL_EXPONENT_MAX 340
-#define NJS_DECIMAL_EXPONENT_DIST 8
-
-
-njs_inline njs_diyfp_t
-njs_d2diyfp(double d)
-{
- int biased_exp;
- uint64_t significand;
- njs_diyfp_t r;
-
- union {
- double d;
- uint64_t u64;
- } u;
-
- u.d = d;
-
- biased_exp = (u.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE;
- significand = u.u64 & NJS_DBL_SIGNIFICAND_MASK;
-
- if (biased_exp != 0) {
- r.significand = significand + NJS_DBL_HIDDEN_BIT;
- r.exp = biased_exp - NJS_DBL_EXPONENT_BIAS;
-
- } else {
- r.significand = significand;
- r.exp = NJS_DBL_EXPONENT_MIN + 1;
- }
-
- return r;
-}
-
-
-njs_inline double
-njs_diyfp2d(njs_diyfp_t v)
-{
- int exp;
- uint64_t significand, biased_exp;
- njs_diyfp_conv_t conv;
-
- exp = v.exp;
- significand = v.significand;
-
- while (significand > NJS_DBL_HIDDEN_BIT + NJS_DBL_SIGNIFICAND_MASK) {
- significand >>= 1;
- exp++;
- }
-
- if (exp >= NJS_DBL_EXPONENT_MAX) {
- return INFINITY;
- }
-
- if (exp < NJS_DBL_EXPONENT_DENORMAL) {
- return 0.0;
- }
-
- while (exp > NJS_DBL_EXPONENT_DENORMAL
- && (significand & NJS_DBL_HIDDEN_BIT) == 0)
- {
- significand <<= 1;
- exp--;
- }
-
- if (exp == NJS_DBL_EXPONENT_DENORMAL
- && (significand & NJS_DBL_HIDDEN_BIT) == 0)
- {
- biased_exp = 0;
-
- } else {
- biased_exp = (uint64_t) (exp + NJS_DBL_EXPONENT_BIAS);
- }
-
- conv.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK)
- | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE);
-
- return conv.d;
-}
-
-
-njs_inline njs_diyfp_t
-njs_diyfp_shift_left(njs_diyfp_t v, unsigned shift)
-{
- return njs_diyfp(v.significand << shift, v.exp - shift);
-}
-
-
-njs_inline njs_diyfp_t
-njs_diyfp_shift_right(njs_diyfp_t v, unsigned shift)
-{
- return njs_diyfp(v.significand >> shift, v.exp + shift);
-}
-
-
-njs_inline njs_diyfp_t
-njs_diyfp_sub(njs_diyfp_t lhs, njs_diyfp_t rhs)
-{
- return njs_diyfp(lhs.significand - rhs.significand, lhs.exp);
-}
-
-
-njs_inline njs_diyfp_t
-njs_diyfp_mul(njs_diyfp_t lhs, njs_diyfp_t rhs)
-{
-#if (NJS_HAVE_UNSIGNED_INT128)
-
- uint64_t l, h;
- njs_uint128_t u128;
-
- u128 = (njs_uint128_t) (lhs.significand)
- * (njs_uint128_t) (rhs.significand);
-
- h = u128 >> 64;
- l = (uint64_t) u128;
-
- /* rounding. */
-
- if (l & ((uint64_t) 1 << 63)) {
- h++;
- }
-
- return njs_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 njs_diyfp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32),
- lhs.exp + rhs.exp + 64);
-
-#endif
-}
-
-
-njs_inline njs_diyfp_t
-njs_diyfp_normalize(njs_diyfp_t v)
-{
- return njs_diyfp_shift_left(v, njs_leading_zeros64(v.significand));
-}
-
-
-njs_diyfp_t njs_cached_power_dec(int exp, int *dec_exp);
-njs_diyfp_t njs_cached_power_bin(int exp, int *dec_exp);
-
-
-#endif /* _NJS_DIYFP_H_INCLUDED_ */
-
/*
- * 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.
+ * Tiny float64 printing and parsing library
*
- * Copyright (C) 2015 Doug Currie
- * based on dtoa_milo.h
- * Copyright (C) 2014 Milo Yip
+ * Copyright (c) 2024 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* 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
+ * 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.
+ *
+ * bellard/quickjs/commit/dbbca3dbf3856938120071225a5e4c906d3177e8
*/
-
#include <njs_main.h>
-#include <njs_diyfp.h>
-#include <njs_dtoa.h>
+#include <njs_cutils.h>
+/*
+ TODO:
+ - test n_digits=101 instead of 100
+ - simplify subnormal handling
+ - reduce max memory usage
+ - free format: could add shortcut if exact result
+ - use 64 bit limb_t when possible
+ - use another algorithm for free format dtoa in base 10 (ryu ?)
+*/
-njs_inline void
-njs_round(char *start, size_t length, uint64_t delta, uint64_t rest,
- uint64_t ten_kappa, uint64_t margin)
-{
- while (rest < margin && delta - rest >= ten_kappa
- && (rest + ten_kappa < margin || /* closer */
- margin - rest > rest + ten_kappa - margin))
- {
- start[length - 1]--;
- rest += ten_kappa;
- }
-}
+#define USE_POW5_TABLE
+/* use fast path to print small integers in free format */
+#define USE_FAST_INT
+#define LIMB_LOG2_BITS 5
-njs_inline void
-njs_round_prec(char *start, size_t length, uint64_t rest, uint64_t ten_kappa,
- uint64_t unit, int *kappa)
-{
- njs_int_t i;
+#define LIMB_BITS (1 << LIMB_LOG2_BITS)
- if (unit >= ten_kappa || ten_kappa - unit <= unit) {
- return;
- }
+typedef int32_t slimb_t;
+typedef uint32_t limb_t;
+typedef uint64_t dlimb_t;
- if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
- return;
- }
+#define LIMB_DIGITS 9
- if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
- start[length - 1]++;
+#define JS_RADIX_MAX 36
- for (i = length - 1; i > 0; --i) {
- if (start[i] != '0' + 10) {
- break;
- }
+#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */
+#define MANT_LEN_MAX 18 /* < 36^100 */
- start[i] = '0';
- start[i - 1]++;
- }
+typedef intptr_t mp_size_t;
- if (start[0] == '0' + 10) {
- start[0] = '1';
- *kappa += 1;
- }
+/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */
+typedef struct {
+ int len; /* >= 1 */
+ limb_t tab[];
+} mpb_t;
+
+static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n)
+{
+ size_t i;
+ limb_t k, a;
+
+ k=b;
+ for(i=0;i<n;i++) {
+ if (k == 0)
+ break;
+ a = tab[i] + k;
+ k = (a < k);
+ tab[i] = a;
}
+ return k;
}
-
-njs_inline int
-njs_dec_count(uint32_t n)
+/* tabr[] = taba[] * b + l. Return the high carry */
+static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t l)
{
- if (n < 10000) {
- if (n < 100) {
- return (n < 10) ? 1 : 2;
+ limb_t i;
+ dlimb_t t;
- } else {
- return (n < 1000) ? 3 : 4;
- }
-
- } else {
- if (n < 1000000) {
- return (n < 100000) ? 5 : 6;
-
- } else {
- if (n < 100000000) {
- return (n < 10000000) ? 7 : 8;
-
- } else {
- return (n < 1000000000) ? 9 : 10;
- }
- }
+ for(i = 0; i < n; i++) {
+ t = (dlimb_t)taba[i] * (dlimb_t)b + l;
+ tabr[i] = t;
+ l = t >> LIMB_BITS;
}
+ return l;
}
+/* WARNING: d must be >= 2^(LIMB_BITS-1) */
+static inline limb_t udiv1norm_init(limb_t d)
+{
+ limb_t a0, a1;
+ a1 = -d - 1;
+ a0 = -1;
+ return (((dlimb_t)a1 << LIMB_BITS) | a0) / d;
+}
-njs_inline size_t
-njs_digit_gen(njs_diyfp_t v, njs_diyfp_t high, uint64_t delta, char *start,
- int *dec_exp)
+/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0
+ / d' with 0 <= a1 < d. */
+static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0,
+ limb_t d, limb_t d_inv)
{
- int kappa;
- char *p;
- uint32_t integer, d;
- uint64_t fraction, rest, margin;
- njs_diyfp_t one;
+ limb_t n1m, n_adj, q, r, ah;
+ dlimb_t a;
+ n1m = ((slimb_t)a0 >> (LIMB_BITS - 1));
+ n_adj = a0 + (n1m & d);
+ a = (dlimb_t)d_inv * (a1 - n1m) + n_adj;
+ q = (a >> LIMB_BITS) + a1;
+ /* compute a - q * r and update q so that the remainder is between
+ 0 and d - 1 */
+ a = ((dlimb_t)a1 << LIMB_BITS) | a0;
+ a = a - (dlimb_t)q * d - d;
+ ah = a >> LIMB_BITS;
+ q += 1 + ah;
+ r = (limb_t)a + (ah & d);
+ *pr = r;
+ return q;
+}
- static const uint64_t pow10[] = {
- 1,
- 10,
- 100,
- 1000,
- 10000,
- 100000,
- 1000000,
- 10000000,
- 100000000,
- 1000000000
- };
+static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t r)
+{
+ slimb_t i;
+ dlimb_t a1;
+ for(i = n - 1; i >= 0; i--) {
+ a1 = ((dlimb_t)r << LIMB_BITS) | taba[i];
+ tabr[i] = a1 / b;
+ r = a1 % b;
+ }
+ return r;
+}
- one = njs_diyfp((uint64_t) 1 << -high.exp, high.exp);
- integer = (uint32_t) (high.significand >> -one.exp);
- fraction = high.significand & (one.significand - 1);
+/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
+ 1 <= shift <= LIMB_BITS - 1 */
+static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n,
+ int shift, limb_t high)
+{
+ mp_size_t i;
+ limb_t l, a;
+
+ njs_assert(shift >= 1 && shift < LIMB_BITS);
+ l = high;
+ for(i = n - 1; i >= 0; i--) {
+ a = tab[i];
+ tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift));
+ l = a;
+ }
+ return l & (((limb_t)1 << shift) - 1);
+}
- margin = njs_diyfp_sub(high, v).significand;
+/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low <
+ 2^shift. */
+static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n,
+ int shift, limb_t low)
+{
+ mp_size_t i;
+ limb_t l, a;
+
+ njs_assert(shift >= 1 && shift < LIMB_BITS);
+ l = low;
+ for(i = 0; i < n; i++) {
+ a = tab[i];
+ tab_r[i] = (a << shift) | l;
+ l = (a >> (LIMB_BITS - shift));
+ }
+ return l;
+}
- p = start;
+static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t r, limb_t b_inv, int shift)
+{
+ slimb_t i;
- kappa = njs_dec_count(integer);
+ if (shift != 0) {
+ r = (r << shift) | mp_shl(tabr, taba, n, shift, 0);
+ }
+ for(i = n - 1; i >= 0; i--) {
+ tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
+ }
+ r >>= shift;
+ return r;
+}
- while (kappa > 0) {
+static __maybe_unused void mpb_dump(const char *str, const mpb_t *a)
+{
+ int i;
+
+ printf("%s= 0x", str);
+ for(i = a->len - 1; i >= 0; i--) {
+ printf("%08x", a->tab[i]);
+ if (i != 0)
+ printf("_");
+ }
+ printf("\n");
+}
- switch (kappa) {
- case 10: d = integer / 1000000000; integer %= 1000000000; break;
- case 9: d = integer / 100000000; integer %= 100000000; break;
- case 8: d = integer / 10000000; integer %= 10000000; break;
- case 7: d = integer / 1000000; integer %= 1000000; break;
- case 6: d = integer / 100000; integer %= 100000; break;
- case 5: d = integer / 10000; integer %= 10000; break;
- case 4: d = integer / 1000; integer %= 1000; break;
- case 3: d = integer / 100; integer %= 100; break;
- case 2: d = integer / 10; integer %= 10; break;
- default: d = integer; integer = 0; break;
- }
+static void mpb_renorm(mpb_t *r)
+{
+ while (r->len > 1 && r->tab[r->len - 1] == 0)
+ r->len--;
+}
- if (d != 0 || p != start) {
- *p++ = '0' + d;
+#ifdef USE_POW5_TABLE
+static const uint32_t pow5_table[17] = {
+ 0x00000005, 0x00000019, 0x0000007d, 0x00000271,
+ 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1,
+ 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51,
+ 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1,
+ 0xa2bc2ec5,
+};
+
+static const uint8_t pow5h_table[4] = {
+ 0x00000001, 0x00000007, 0x00000023, 0x000000b1,
+};
+
+static const uint32_t pow5_inv_table[13] = {
+ 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1,
+ 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23,
+ 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812,
+ 0xc25c2684,
+};
+#endif
+
+/* return a^b */
+static uint64_t pow_ui(uint32_t a, uint32_t b)
+{
+ int i, n_bits;
+ uint64_t r;
+ if (b == 0)
+ return 1;
+ if (b == 1)
+ return a;
+#ifdef USE_POW5_TABLE
+ if ((a == 5 || a == 10) && b <= 17) {
+ r = pow5_table[b - 1];
+ if (b >= 14) {
+ r |= (uint64_t)pow5h_table[b - 14] << 32;
}
+ if (a == 10)
+ r <<= b;
+ return r;
+ }
+#endif
+ r = a;
+ n_bits = 32 - clz32(b);
+ for(i = n_bits - 2; i >= 0; i--) {
+ r *= r;
+ if ((b >> i) & 1)
+ r *= a;
+ }
+ return r;
+}
- kappa--;
-
- rest = ((uint64_t) integer << -one.exp) + fraction;
-
- if (rest < delta) {
- *dec_exp += kappa;
- njs_round(start, p - start, delta, rest, pow10[kappa] << -one.exp,
- margin);
- return p - start;
- }
+static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b)
+{
+ uint32_t r_inv, r;
+ int shift;
+#ifdef USE_POW5_TABLE
+ if (a == 5 && b >= 1 && b <= 13) {
+ r = pow5_table[b - 1];
+ shift = clz32(r);
+ r <<= shift;
+ r_inv = pow5_inv_table[b - 1];
+ } else
+#endif
+ {
+ r = pow_ui(a, b);
+ shift = clz32(r);
+ r <<= shift;
+ r_inv = udiv1norm_init(r);
}
+ *pshift = shift;
+ *pr_inv = r_inv;
+ return r;
+}
- /* kappa = 0. */
+enum {
+ JS_RNDN, /* round to nearest, ties to even */
+ JS_RNDNA, /* round to nearest, ties away from zero */
+ JS_RNDZ,
+};
- for ( ;; ) {
- fraction *= 10;
- delta *= 10;
+static int mpb_get_bit(const mpb_t *r, int k)
+{
+ int l;
+
+ l = (unsigned)k / LIMB_BITS;
+ k = k & (LIMB_BITS - 1);
+ if (l >= r->len)
+ return 0;
+ else
+ return (r->tab[l] >> k) & 1;
+}
- d = (uint32_t) (fraction >> -one.exp);
+/* compute round(r / 2^shift). 'shift' can be negative */
+static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode)
+{
+ int l, i;
- if (d != 0 || p != start) {
- *p++ = '0' + d;
+ if (shift == 0)
+ return;
+ if (shift < 0) {
+ shift = -shift;
+ l = (unsigned)shift / LIMB_BITS;
+ shift = shift & (LIMB_BITS - 1);
+ if (shift != 0) {
+ r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0);
+ r->len++;
+ mpb_renorm(r);
+ }
+ if (l > 0) {
+ for(i = r->len - 1; i >= 0; i--)
+ r->tab[i + l] = r->tab[i];
+ for(i = 0; i < l; i++)
+ r->tab[i] = 0;
+ r->len += l;
+ }
+ } else {
+ limb_t bit1, bit2;
+ int k, add_one;
+
+ switch(rnd_mode) {
+ default:
+ case JS_RNDZ:
+ add_one = 0;
+ break;
+ case JS_RNDN:
+ case JS_RNDNA:
+ bit1 = mpb_get_bit(r, shift - 1);
+ if (bit1) {
+ if (rnd_mode == JS_RNDNA) {
+ bit2 = 1;
+ } else {
+ /* bit2 = oring of all the bits after bit1 */
+ bit2 = 0;
+ if (shift >= 2) {
+ k = shift - 1;
+ l = (unsigned)k / LIMB_BITS;
+ k = k & (LIMB_BITS - 1);
+ for(i = 0; i < min_int(l, r->len); i++)
+ bit2 |= r->tab[i];
+ if (l < r->len)
+ bit2 |= r->tab[l] & (((limb_t)1 << k) - 1);
+ }
+ }
+ if (bit2) {
+ add_one = 1;
+ } else {
+ /* round to even */
+ add_one = mpb_get_bit(r, shift);
+ }
+ } else {
+ add_one = 0;
+ }
+ break;
}
- fraction &= one.significand - 1;
- kappa--;
-
- if (fraction < delta) {
- *dec_exp += kappa;
- margin *= (-kappa < 10) ? pow10[-kappa] : 0;
- njs_round(start, p - start, delta, fraction, one.significand,
- margin);
- return p - start;
+ l = (unsigned)shift / LIMB_BITS;
+ shift = shift & (LIMB_BITS - 1);
+ if (l >= r->len) {
+ r->len = 1;
+ r->tab[0] = add_one;
+ } else {
+ if (l > 0) {
+ r->len -= l;
+ for(i = 0; i < r->len; i++)
+ r->tab[i] = r->tab[i + l];
+ }
+ if (shift != 0) {
+ mp_shr(r->tab, r->tab, r->len, shift, 0);
+ mpb_renorm(r);
+ }
+ if (add_one) {
+ limb_t a;
+ a = mp_add_ui(r->tab, 1, r->len);
+ if (a)
+ r->tab[r->len++] = a;
+ }
}
}
}
-
-njs_inline size_t
-njs_digit_gen_prec(njs_diyfp_t v, size_t prec, char *start, int *dec_exp)
+/* return -1, 0 or 1 */
+static int mpb_cmp(const mpb_t *a, const mpb_t *b)
{
- int kappa;
- char *p;
- uint32_t integer, divisor;
- uint64_t fraction, rest, error;
- njs_diyfp_t one;
-
- static const uint64_t pow10[] = {
- 1,
- 10,
- 100,
- 1000,
- 10000,
- 100000,
- 1000000,
- 10000000,
- 100000000,
- 1000000000
- };
-
- one = njs_diyfp((uint64_t) 1 << -v.exp, v.exp);
- integer = (uint32_t) (v.significand >> -one.exp);
- fraction = v.significand & (one.significand - 1);
-
- error = 1;
-
- p = start;
-
- kappa = njs_dec_count(integer);
-
- while (kappa > 0) {
- divisor = pow10[kappa - 1];
-
- *p++ = '0' + integer / divisor;
-
- integer %= divisor;
-
- kappa--;
- prec--;
-
- if (prec == 0) {
- rest = ((uint64_t) integer << -one.exp) + fraction;
- njs_round_prec(start, p - start, rest, pow10[kappa] << -one.exp,
- error, &kappa);
-
- *dec_exp += kappa;
- return p - start;
+ mp_size_t i;
+ if (a->len < b->len)
+ return -1;
+ else if (a->len > b->len)
+ return 1;
+ for(i = a->len - 1; i >= 0; i--) {
+ if (a->tab[i] != b->tab[i]) {
+ if (a->tab[i] < b->tab[i])
+ return -1;
+ else
+ return 1;
}
}
-
- /* kappa = 0. */
-
- while (prec > 0 && fraction > error) {
- fraction *= 10;
- error *= 10;
-
- *p++ = '0' + (fraction >> -one.exp);
-
- fraction &= one.significand - 1;
- kappa--;
- prec--;
- }
-
- njs_round_prec(start, p - start, fraction, one.significand, error, &kappa);
-
- *dec_exp += kappa;
-
- return p - start;
+ return 0;
}
+static void mpb_set_u64(mpb_t *r, uint64_t m)
+{
+#if LIMB_BITS == 64
+ r->tab[0] = m;
+ r->len = 1;
+#else
+ r->tab[0] = m;
+ r->tab[1] = m >> LIMB_BITS;
+ if (r->tab[1] == 0)
+ r->len = 1;
+ else
+ r->len = 2;
+#endif
+}
-njs_inline njs_diyfp_t
-njs_diyfp_normalize_boundary(njs_diyfp_t v)
+static uint64_t mpb_get_u64(mpb_t *r)
{
- while ((v.significand & (NJS_DBL_HIDDEN_BIT << 1)) == 0) {
- v.significand <<= 1;
- v.exp--;
+#if LIMB_BITS == 64
+ return r->tab[0];
+#else
+ if (r->len == 1) {
+ return r->tab[0];
+ } else {
+ return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS);
}
-
- return njs_diyfp_shift_left(v, NJS_SIGNIFICAND_SHIFT - 2);
+#endif
}
-
-njs_inline void
-njs_diyfp_normalize_boundaries(njs_diyfp_t v, njs_diyfp_t* minus,
- njs_diyfp_t* plus)
+/* floor_log2() = position of the first non zero bit or -1 if zero. */
+static int mpb_floor_log2(mpb_t *a)
{
- njs_diyfp_t pl, mi;
-
- pl = njs_diyfp_normalize_boundary(njs_diyfp((v.significand << 1) + 1,
- v.exp - 1));
-
- if (v.significand == NJS_DBL_HIDDEN_BIT) {
- mi = njs_diyfp((v.significand << 2) - 1, v.exp - 2);
+ limb_t v;
+ v = a->tab[a->len - 1];
+ if (v == 0)
+ return -1;
+ else
+ return a->len * LIMB_BITS - 1 - clz32(v);
+}
+#define MUL_LOG2_RADIX_BASE_LOG2 24
+
+/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */
+static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = {
+ 0x000000, 0xa1849d, 0x000000, 0x6e40d2,
+ 0x6308c9, 0x5b3065, 0x000000, 0x50c24e,
+ 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54,
+ 0x433d00, 0x418677, 0x000000, 0x3ea16b,
+ 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899,
+ 0x39680b, 0x3897b3, 0x37d5af, 0x372069,
+ 0x367686, 0x35d6df, 0x354072, 0x34b261,
+ 0x342bea, 0x33ac62, 0x000000, 0x32bfd9,
+ 0x3251dd, 0x31e8d6, 0x318465,
+};
+
+/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */
+static int mul_log2_radix(int a, int radix)
+{
+ int radix_bits, mult;
+
+ if ((radix & (radix - 1)) == 0) {
+ /* if the radix is a power of two better to do it exactly */
+ radix_bits = 31 - clz32(radix);
+ if (a < 0)
+ a -= radix_bits - 1;
+ return a / radix_bits;
} else {
- mi = njs_diyfp((v.significand << 1) - 1, v.exp - 1);
+ mult = mul_log2_radix_table[radix - 2];
+ return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2;
}
-
- mi.significand <<= mi.exp - pl.exp;
- mi.exp = pl.exp;
-
- *plus = pl;
- *minus = mi;
}
-
-/*
- * Grisu2 produces optimal (shortest) decimal representation for 99.8%
- * of IEEE doubles. For remaining 0.2% bignum algorithm like Dragon4 is requred.
- */
-njs_inline size_t
-njs_grisu2(double value, char *start, int *point)
+#if 0
+static void build_mul_log2_radix_table(void)
{
- int dec_exp;
- size_t length;
- njs_diyfp_t v, low, high, ten_mk, scaled_v, scaled_low, scaled_high;
-
- v = njs_d2diyfp(value);
-
- njs_diyfp_normalize_boundaries(v, &low, &high);
-
- ten_mk = njs_cached_power_bin(high.exp, &dec_exp);
-
- scaled_v = njs_diyfp_mul(njs_diyfp_normalize(v), ten_mk);
- scaled_low = njs_diyfp_mul(low, ten_mk);
- scaled_high = njs_diyfp_mul(high, ten_mk);
-
- scaled_low.significand++;
- scaled_high.significand--;
-
- length = njs_digit_gen(scaled_v, scaled_high,
- scaled_high.significand - scaled_low.significand,
- start, &dec_exp);
-
- *point = length + dec_exp;
-
- return length;
+ int base, radix, mult, col, base_log2;
+
+ base_log2 = 24;
+ base = 1 << base_log2;
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ if ((radix & (radix - 1)) == 0)
+ mult = 0;
+ else
+ mult = lrint((double)base / log2(radix));
+ printf("0x%06x, ", mult);
+ if (++col == 4) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ printf("\n");
}
-
-njs_inline size_t
-njs_grisu2_prec(double value, char *start, size_t prec, int *point)
+static void mul_log2_radix_test(void)
{
- int dec_exp;
- size_t length;
- njs_diyfp_t v, ten_mk, scaled_v;
-
- v = njs_diyfp_normalize(njs_d2diyfp(value));
-
- ten_mk = njs_cached_power_bin(v.exp, &dec_exp);
-
- scaled_v = njs_diyfp_mul(v, ten_mk);
-
- length = njs_digit_gen_prec(scaled_v, prec, start, &dec_exp);
-
- *point = length + dec_exp;
-
- return length;
+ int radix, i, ref, r;
+
+ for(radix = 2; radix <= 36; radix++) {
+ for(i = -2048; i <= 2047; i++) {
+ ref = (int)floor((double)i / log2(radix));
+ r = mul_log2_radix(i, radix);
+ if (ref != r) {
+ printf("ERROR: radix=%d i=%d r=%d ref=%d\n",
+ radix, i, r, ref);
+ exit(1);
+ }
+ }
+ }
+ if (0)
+ build_mul_log2_radix_table();
}
+#endif
-
-njs_inline size_t
-njs_write_exponent(int exp, char *start)
+static void u32toa_len(char *buf, uint32_t n, size_t len)
{
- char *p;
- size_t length;
- uint32_t u32;
- char buf[4];
-
- /* -324 <= exp <= 308. */
+ int digit, i;
+ for(i = len - 1; i >= 0; i--) {
+ digit = n % 10;
+ n = n / 10;
+ buf[i] = digit + '0';
+ }
+}
- if (exp < 0) {
- *start++ = '-';
- exp = -exp;
+/* for power of 2 radixes. len >= 1 */
+static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len)
+{
+ int digit, i;
+ unsigned int mask;
+
+ mask = (1 << radix_bits) - 1;
+ for(i = len - 1; i >= 0; i--) {
+ digit = n & mask;
+ n >>= radix_bits;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ buf[i] = digit;
+ }
+}
+/* len >= 1. 2 <= radix <= 36 */
+static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len)
+{
+ int digit, i;
+
+ if (radix == 10) {
+ /* specific case with constant divisor */
+#if LIMB_BITS == 32
+ u32toa_len(buf, n, len);
+#else
+ /* XXX: optimize */
+ for(i = len - 1; i >= 0; i--) {
+ digit = (limb_t)n % 10;
+ n = (limb_t)n / 10;
+ buf[i] = digit + '0';
+ }
+#endif
} else {
- *start++ = '+';
+ for(i = len - 1; i >= 0; i--) {
+ digit = (limb_t)n % radix;
+ n = (limb_t)n / radix;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ buf[i] = digit;
+ }
}
+}
- u32 = exp;
- p = buf + njs_length(buf);
-
+size_t njs_u32toa(char *buf, uint32_t n)
+{
+ char buf1[10], *q;
+ size_t len;
+
+ q = buf1 + sizeof(buf1);
do {
- *--p = u32 % 10 + '0';
- u32 /= 10;
- } while (u32 != 0);
-
- length = buf + njs_length(buf) - p;
-
- memcpy(start, p, length);
-
- return length + 1;
+ *--q = n % 10 + '0';
+ n /= 10;
+ } while (n != 0);
+ len = buf1 + sizeof(buf1) - q;
+ memcpy(buf, q, len);
+ return len;
}
-
-njs_inline size_t
-njs_dtoa_format(char *start, size_t len, int point)
+size_t njs_i32toa(char *buf, int32_t n)
{
- int offset, length;
- size_t size;
-
- length = (int) len;
-
- if (length <= point && point <= 21) {
-
- /* 1234e7 -> 12340000000 */
+ if (n >= 0) {
+ return njs_u32toa(buf, n);
+ } else {
+ buf[0] = '-';
+ return njs_u32toa(buf + 1, -(uint32_t)n) + 1;
+ }
+}
- if (point - length > 0) {
- njs_memset(&start[length], '0', point - length);
+#ifdef USE_FAST_INT
+size_t njs_u64toa(char *buf, uint64_t n)
+{
+ if (n < 0x100000000) {
+ return njs_u32toa(buf, n);
+ } else {
+ uint64_t n1;
+ char *q = buf;
+ uint32_t n2;
+
+ n1 = n / 1000000000;
+ n %= 1000000000;
+ if (n1 >= 0x100000000) {
+ n2 = n1 / 1000000000;
+ n1 = n1 % 1000000000;
+ /* at most two digits */
+ if (n2 >= 10) {
+ *q++ = n2 / 10 + '0';
+ n2 %= 10;
+ }
+ *q++ = n2 + '0';
+ u32toa_len(q, n1, 9);
+ q += 9;
+ } else {
+ q += njs_u32toa(q, n1);
}
-
- return point;
+ u32toa_len(q, n, 9);
+ q += 9;
+ return q - buf;
}
+}
- if (0 < point && point <= 21) {
-
- /* 1234e-2 -> 12.34 */
-
- memmove(&start[point + 1], &start[point], length - point);
- start[point] = '.';
-
- return length + 1;
+size_t njs_i64toa(char *buf, int64_t n)
+{
+ if (n >= 0) {
+ return njs_u64toa(buf, n);
+ } else {
+ buf[0] = '-';
+ return njs_u64toa(buf + 1, -(uint64_t)n) + 1;
}
+}
- if (-6 < point && point <= 0) {
-
- /* 1234e-6 -> 0.001234 */
-
- offset = 2 - point;
- memmove(&start[offset], start, length);
-
- start[0] = '0';
- start[1] = '.';
-
- if (offset - 2 > 0) {
- njs_memset(&start[2], '0', offset - 2);
- }
-
- return length + offset;
+/* XXX: only tested for 1 <= n < 2^53 */
+size_t njs_u64toa_radix(char *buf, uint64_t n, unsigned int radix)
+{
+ int radix_bits, l;
+ if (likely(radix == 10))
+ return njs_u64toa(buf, n);
+ if ((radix & (radix - 1)) == 0) {
+ radix_bits = 31 - clz32(radix);
+ if (n == 0)
+ l = 1;
+ else
+ l = (64 - clz64(n) + radix_bits - 1) / radix_bits;
+ u64toa_bin_len(buf, n, radix_bits, l);
+ return l;
+ } else {
+ char buf1[41], *q; /* maximum length for radix = 3 */
+ size_t len;
+ int digit;
+ q = buf1 + sizeof(buf1);
+ do {
+ digit = n % radix;
+ n /= radix;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ *--q = digit;
+ } while (n != 0);
+ len = buf1 + sizeof(buf1) - q;
+ memcpy(buf, q, len);
+ return len;
}
+}
- /* 1234e30 -> 1.234e33 */
-
- if (length == 1) {
-
- /* 1e30 */
-
- start[1] = 'e';
-
- size = njs_write_exponent(point - 1, &start[2]);
-
- return size + 2;
+size_t njs_i64toa_radix(char *buf, int64_t n, unsigned int radix)
+{
+ if (n >= 0) {
+ return njs_u64toa_radix(buf, n, radix);
+ } else {
+ buf[0] = '-';
+ return njs_u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1;
}
-
- memmove(&start[2], &start[1], length - 1);
- start[1] = '.';
- start[length + 1] = 'e';
-
- size = njs_write_exponent(point - 1, &start[length + 2]);
-
- return size + length + 2;
}
-
-
-njs_inline size_t
-njs_dtoa_exp_format(char *start, int exponent, size_t prec, size_t len)
+#endif /* USE_FAST_INT */
+
+static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
+#if LIMB_BITS == 32
+32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+#else
+64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
+#endif
+};
+
+static const uint32_t radix_base_table[JS_RADIX_MAX - 1] = {
+ 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
+ 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
+ 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
+ 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
+ 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
+ 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
+ 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
+ 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
+ 0x5c13d840, 0x6d91b519, 0x81bf1000,
+};
+
+/* XXX: remove the table ? */
+static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = {
+ 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12,
+};
+
+/* we limit the maximum number of significant digits for atod to about
+ 128 bits of precision for non power of two bases. The only
+ requirement for Javascript is at least 20 digits in base 10. For
+ power of two bases, we do an exact rounding in all the cases. */
+static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = {
+ 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24,
+};
+
+/* if abs(d) >= B^max_exponent, it is an overflow */
+static const int16_t max_exponent[JS_RADIX_MAX - 1] = {
+ 1024, 647, 512, 442, 397, 365, 342, 324,
+ 309, 297, 286, 277, 269, 263, 256, 251,
+ 246, 242, 237, 234, 230, 227, 224, 221,
+ 218, 216, 214, 211, 209, 207, 205, 203,
+ 202, 200, 199,
+};
+
+/* if abs(d) <= B^min_exponent, it is an underflow */
+static const int16_t min_exponent[JS_RADIX_MAX - 1] = {
+-1075, -679, -538, -463, -416, -383, -359, -340,
+ -324, -311, -300, -291, -283, -276, -269, -263,
+ -258, -254, -249, -245, -242, -238, -235, -232,
+ -229, -227, -224, -222, -220, -217, -215, -214,
+ -212, -210, -208,
+};
+
+#if 0
+void build_tables(void)
{
- char *p;
-
- p = &start[len];
- if (prec != 1) {
- memmove(&start[2], &start[1], len - 1);
- start[1] = '.';
- p++;
+ int r, j, radix, n, col, i;
+
+ /* radix_base_table */
+ for(radix = 2; radix <= 36; radix++) {
+ r = 1;
+ for(j = 0; j < digits_per_limb_table[radix - 2]; j++) {
+ r *= radix;
+ }
+ printf(" 0x%08x,", r);
+ if ((radix % 4) == 1)
+ printf("\n");
}
+ printf("\n");
- njs_memset(p, '0', prec - len);
- p += prec - len;
-
- *p++ = 'e';
+ /* dtoa_max_digits_table */
+ for(radix = 2; radix <= 36; radix++) {
+ /* Note: over estimated when the radix is a power of two */
+ printf(" %d,", 1 + (int)ceil(53.0 / log2(radix)));
+ }
+ printf("\n");
- return prec + 1 + (prec != 1) + njs_write_exponent(exponent, p);
+ /* atod_max_digits_table */
+ for(radix = 2; radix <= 36; radix++) {
+ if ((radix & (radix - 1)) == 0) {
+ /* 64 bits is more than enough */
+ n = (int)floor(64.0 / log2(radix));
+ } else {
+ n = (int)floor(128.0 / log2(radix));
+ }
+ printf(" %d,", n);
+ }
+ printf("\n");
+
+ printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n");
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ printf("%5d, ", (int)ceil(1024 / log2(radix)));
+ if (++col == 8) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n");
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ printf("%5d, ", (int)floor(-1075 / log2(radix)));
+ if (++col == 8) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ printf("static const uint32_t pow5_table[16] = {\n");
+ col = 0;
+ for(i = 2; i <= 17; i++) {
+ r = 1;
+ for(j = 0; j < i; j++) {
+ r *= 5;
+ }
+ printf("0x%08x, ", r);
+ if (++col == 4) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ /* high part */
+ printf("static const uint8_t pow5h_table[4] = {\n");
+ col = 0;
+ for(i = 14; i <= 17; i++) {
+ uint64_t r1;
+ r1 = 1;
+ for(j = 0; j < i; j++) {
+ r1 *= 5;
+ }
+ printf("0x%08x, ", (uint32_t)(r1 >> 32));
+ if (++col == 4) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
}
+#endif
-
-njs_inline size_t
-njs_dtoa_prec_format(char *start, size_t prec, size_t len, int point)
+/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits,
+ the dot is not displayed. 'a' is modified. */
+static int output_digits(char *buf,
+ mpb_t *a, int radix, int n_digits1,
+ int dot_pos)
{
- int exponent;
- size_t m, rest;
-
- exponent = point - 1;
+ int n_digits, digits_per_limb, radix_bits, n, len;
- if (exponent < -6 || exponent >= (int) prec) {
- return njs_dtoa_exp_format(start, exponent, prec, len);
+ n_digits = n_digits1;
+ if ((radix & (radix - 1)) == 0) {
+ /* radix = 2^radix_bits */
+ radix_bits = 31 - clz32(radix);
+ } else {
+ radix_bits = 0;
}
-
- /* Fixed notation. */
-
- if (point <= 0) {
-
- /* 1234e-2 => 0.001234000 */
-
- memmove(&start[2 + (-point)], start, len);
- start[0] = '0';
- start[1] = '.';
-
- njs_memset(&start[2], '0', -point);
-
- if (prec > len) {
- njs_memset(&start[2 + (-point) + len], '0', prec - len);
+ digits_per_limb = digits_per_limb_table[radix - 2];
+ if (radix_bits != 0) {
+ for(;;) {
+ n = min_int(n_digits, digits_per_limb);
+ n_digits -= n;
+ u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n);
+ if (n_digits == 0)
+ break;
+ mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ);
+ }
+ } else {
+ limb_t r;
+ while (n_digits != 0) {
+ n = min_int(n_digits, digits_per_limb);
+ n_digits -= n;
+ r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0);
+ mpb_renorm(a);
+ limb_to_a(buf + n_digits, r, radix, n);
}
-
- return prec + 2 + (-point);
}
- if (point >= (int) len) {
-
- /* TODO: (2**96).toPrecision(45) not enough precision, BigInt needed. */
-
- njs_memset(&start[len], '0', point - len);
-
- if (point < (int) prec) {
- start[point] = '.';
+ /* add the dot */
+ len = n_digits1;
+ if (dot_pos != n_digits1) {
+ memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos);
+ buf[dot_pos] = '.';
+ len++;
+ }
+ return len;
+}
- njs_memset(&start[point + 1], '0', prec - point);
+/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f *
+ 2^-e_offset. 'f' can be negative. */
+static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, BOOL is_int, int e)
+{
+ int e_offset, d, n, n0;
+
+ e_offset = -f * radix_shift;
+ if (radix1 != 1) {
+ d = digits_per_limb_table[radix1 - 2];
+ if (f >= 0) {
+ limb_t h, b;
+
+ b = 0;
+ n0 = 0;
+ while (f != 0) {
+ n = min_int(f, d);
+ if (n != n0) {
+ b = pow_ui(radix1, n);
+ n0 = n;
+ }
+ h = mp_mul1(a->tab, a->tab, a->len, b, 0);
+ if (h != 0) {
+ a->tab[a->len++] = h;
+ }
+ f -= n;
+ }
+ } else {
+ int extra_bits, l, shift;
+ limb_t r, rem, b, b_inv;
+
+ f = -f;
+ l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */
+ e_offset += l * LIMB_BITS;
+ if (!is_int) {
+ /* at least 'e' bits are needed in the final result for rounding */
+ extra_bits = max_int(e - mpb_floor_log2(a), 0);
+ } else {
+ /* at least two extra bits are needed in the final result
+ for rounding */
+ extra_bits = max_int(2 + e - e_offset, 0);
+ }
+ e_offset += extra_bits;
+ mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ);
+
+ b = 0;
+ b_inv = 0;
+ shift = 0;
+ n0 = 0;
+ rem = 0;
+ while (f != 0) {
+ n = min_int(f, d);
+ if (n != n0) {
+ b = pow_ui_inv(&b_inv, &shift, radix1, n);
+ n0 = n;
+ }
+ r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift);
+ rem |= r;
+ mpb_renorm(a);
+ f -= n;
+ }
+ /* if the remainder is non zero, use it for rounding */
+ a->tab[0] |= (rem != 0);
}
-
- } else if (point < (int) prec) {
-
- /* 123456 -> 123.45600 */
-
- m = njs_min((int) len, point);
- rest = njs_min(len, prec) - m;
- memmove(&start[m + 1], &start[m], rest);
-
- start[m] = '.';
-
- njs_memset(&start[m + rest + 1], '0', prec - m - rest);
}
-
- return prec + (point < (int) prec);
+ return e_offset;
}
-
-size_t
-njs_dtoa(double value, char *start)
+/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */
+static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f,
+ int rnd_mode)
{
- int point, minus;
- char *p;
- size_t length;
-
- /* Not handling NaN and inf. */
+ int e_offset;
- minus = 0;
- p = start;
-
- if (value == 0) {
- *p++ = '0';
+ mpb_set_u64(tmp1, m);
+ e_offset = mul_pow(tmp1, radix1, radix_shift, f, TRUE, e);
+ mpb_shr_round(tmp1, -e + e_offset, rnd_mode);
+}
- return p - start;
- }
+/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */
+static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode)
+{
+ int e;
+ uint64_t m;
- if (signbit(value)) {
- *p++ = '-';
- value = -value;
- minus = 1;
+ if (a->tab[0] == 0 && a->len == 1) {
+ /* zero result */
+ m = 0;
+ e = 0; /* don't care */
+ } else {
+ int prec, prec1, e_min;
+ e = mpb_floor_log2(a) + 1 - e_offset;
+ prec1 = 53;
+ e_min = -1021;
+ if (e < e_min) {
+ /* subnormal result or zero */
+ prec = prec1 - (e_min - e);
+ } else {
+ prec = prec1;
+ }
+ mpb_shr_round(a, e + e_offset - prec, rnd_mode);
+ m = mpb_get_u64(a);
+ m <<= (53 - prec);
+ /* mantissa overflow due to rounding */
+ if (m >= (uint64_t)1 << 53) {
+ m >>= 1;
+ e++;
+ }
}
+ *pe = e;
+ return m;
+}
- length = njs_grisu2(value, p, &point);
+/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52
+ <= m < 2^53 or m = 0.
+ 'a' is modified. */
+static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a,
+ int radix1, int radix_shift, int f, int rnd_mode)
+{
+ int e_offset;
- return njs_dtoa_format(p, length, point) + minus;
+ e_offset = mul_pow(a, radix1, radix_shift, f, FALSE, 55);
+ return round_to_d(pe, a, e_offset, rnd_mode);
}
+#ifdef JS_DTOA_DUMP_STATS
+static int out_len_count[17];
-/*
- * TODO: For prec > 16 result maybe rounded. To support prec > 16 Bignum
- * support is requred.
- */
-size_t
-njs_dtoa_precision(double value, char *start, size_t prec)
+void js_dtoa_dump_stats(void)
{
- int point, minus;
- char *p;
- size_t length;
-
- /* Not handling NaN and inf. */
+ int i, sum;
+ sum = 0;
+ for(i = 0; i < 17; i++)
+ sum += out_len_count[i];
+ for(i = 0; i < 17; i++) {
+ printf("%2d %8d %5.2f%%\n",
+ i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100);
+ }
+}
+#endif
- p = start;
- minus = 0;
+/* return a maximum bound of the string length. The bound depends on
+ 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED
+ is enabled. */
+int njs_dtoa_max_len(double d, int radix, int n_digits, int flags)
+{
+ int fmt = flags & JS_DTOA_FORMAT_MASK;
+ int n, e;
+ uint64_t a;
- if (value != 0) {
- if (value < 0) {
- *p++ = '-';
- value = -value;
- minus = 1;
+ if (fmt != JS_DTOA_FORMAT_FRAC) {
+ if (fmt == JS_DTOA_FORMAT_FREE) {
+ n = dtoa_max_digits_table[radix - 2];
+ } else {
+ n = n_digits;
+ }
+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) {
+ /* no exponential */
+ a = float64_as_uint64(d);
+ e = (a >> 52) & 0x7ff;
+ if (e == 0x7ff) {
+ /* NaN, Infinity */
+ n = 0;
+ } else {
+ e -= 1023;
+ /* XXX: adjust */
+ n += 10 + abs(mul_log2_radix(e - 1, radix));
+ }
+ } else {
+ /* extra: sign, 1 dot and exponent "e-1000" */
+ n += 1 + 1 + 6;
}
-
- length = njs_grisu2_prec(value, p, prec, &point);
-
} else {
- start[0] = '0';
- length = 1;
- point = 1;
+ a = float64_as_uint64(d);
+ e = (a >> 52) & 0x7ff;
+ if (e == 0x7ff) {
+ /* NaN, Infinity */
+ n = 0;
+ } else {
+ /* high bound for the integer part */
+ e -= 1023;
+ /* x < 2^(e + 1) */
+ if (e < 0) {
+ n = 1;
+ } else {
+ n = 2 + mul_log2_radix(e - 1, radix);
+ }
+ /* sign, extra digit, 1 dot */
+ n += 1 + 1 + 1 + n_digits;
+ }
}
-
- return njs_dtoa_prec_format(p, prec, length, point) + minus;
+ return max_int(n, 9); /* also include NaN and [-]Infinity */
}
-
-size_t
-njs_dtoa_exponential(double value, char *start, njs_int_t frac)
+#if defined(__SANITIZE_ADDRESS__) && 0
+static void *dtoa_malloc(uint64_t **pptr, size_t size)
{
- int point, minus;
- char *p;
- size_t length;
-
- /* Not handling NaN and inf. */
+ return malloc(size);
+}
+static void dtoa_free(void *ptr)
+{
+ free(ptr);
+}
+#else
+static void *dtoa_malloc(uint64_t **pptr, size_t size)
+{
+ void *ret;
+ ret = *pptr;
+ *pptr += (size + 7) / 8;
+ return ret;
+}
- p = start;
- minus = 0;
+static void dtoa_free(void *ptr)
+{
+}
+#endif
- if (value != 0) {
- if (value < 0) {
- *p++ = '-';
- value = -value;
- minus = 1;
+/* return the length */
+int njs_dtoa2(char *buf, double d, int radix, int n_digits, int flags,
+ JSDTOATempMem *tmp_mem)
+{
+ uint64_t a, m, *mptr = tmp_mem->mem;
+ int e, sgn, l, E, P, i, E_max, radix1, radix_shift;
+ char *q;
+ mpb_t *tmp1, *mant_max;
+ int fmt = flags & JS_DTOA_FORMAT_MASK;
+
+ tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);
+ mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX);
+ njs_assert((size_t) (mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0]));
+
+ radix_shift = ctz32(radix);
+ radix1 = radix >> radix_shift;
+ a = float64_as_uint64(d);
+ sgn = a >> 63;
+ e = (a >> 52) & 0x7ff;
+ m = a & (((uint64_t)1 << 52) - 1);
+ q = buf;
+ if (e == 0x7ff) {
+ if (m == 0) {
+ if (sgn)
+ *q++ = '-';
+ memcpy(q, "Infinity", 8);
+ q += 8;
+ } else {
+ memcpy(q, "NaN", 3);
+ q += 3;
+ }
+ goto done;
+ } else if (e == 0) {
+ if (m == 0) {
+ tmp1->len = 1;
+ tmp1->tab[0] = 0;
+ E = 1;
+ if (fmt == JS_DTOA_FORMAT_FREE)
+ P = 1;
+ else if (fmt == JS_DTOA_FORMAT_FRAC)
+ P = n_digits + 1;
+ else
+ P = n_digits;
+ /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */
+ if (sgn && (flags & JS_DTOA_MINUS_ZERO))
+ *q++ = '-';
+ goto output;
}
+ /* denormal number: convert to a normal number */
+ l = clz64(m) - 11;
+ e -= l - 1;
+ m <<= l;
+ } else {
+ m |= (uint64_t)1 << 52;
+ }
+ if (sgn)
+ *q++ = '-';
+ /* remove the bias */
+ e -= 1022;
+ /* d = 2^(e-53)*m */
+ // printf("m=0x%016" PRIx64 " e=%d\n", m, e);
+#ifdef USE_FAST_INT
+ if (fmt == JS_DTOA_FORMAT_FREE &&
+ e >= 1 && e <= 53 &&
+ (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 &&
+ (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) {
+ m >>= 53 - e;
+ /* 'm' is never zero */
+ q += njs_u64toa_radix(q, m, radix);
+ goto done;
+ }
+#endif
+
+ /* this choice of E implies F=round(x*B^(P-E) is such as:
+ B^(P-1) <= F < 2.B^P. */
+ E = 1 + mul_log2_radix(e - 1, radix);
+
+ if (fmt == JS_DTOA_FORMAT_FREE) {
+ int P_max, E0, e1, E_found, P_found;
+ uint64_t m1, mant_found, mant, mant_max1;
+ /* P_max is guarranteed to work by construction */
+ P_max = dtoa_max_digits_table[radix - 2];
+ E0 = E;
+ E_found = 0;
+ P_found = 0;
+ mant_found = 0;
+ /* find the minimum number of digits by successive tries */
+ P = P_max; /* P_max is guarateed to work */
+ for(;;) {
+ /* mant_max always fits on 64 bits */
+ mant_max1 = pow_ui(radix, P);
+ /* compute the mantissa in base B */
+ E = E0;
+ for(;;) {
+ /* XXX: add inexact flag */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN);
+ mant = mpb_get_u64(tmp1);
+ if (mant < mant_max1)
+ break;
+ E++; /* at most one iteration is possible */
+ }
+ /* remove useless trailing zero digits */
+ while ((mant % radix) == 0) {
+ mant /= radix;
+ P--;
+ }
+ /* garanteed to work for P = P_max */
+ if (P_found == 0)
+ goto prec_found;
+ /* convert back to base 2 */
+ mpb_set_u64(tmp1, mant);
+ m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN);
+ // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1);
+ /* Note: (m, e) is never zero here, so the exponent for m1
+ = 0 does not matter */
+ if (m1 == m && e1 == e) {
+ prec_found:
+ P_found = P;
+ E_found = E;
+ mant_found = mant;
+ if (P == 1)
+ break;
+ P--; /* try lower exponent */
+ } else {
+ break;
+ }
+ }
+ P = P_found;
+ E = E_found;
+ mpb_set_u64(tmp1, mant_found);
+#ifdef JS_DTOA_DUMP_STATS
+ if (radix == 10) {
+ out_len_count[P - 1]++;
+ }
+#endif
+ } else if (fmt == JS_DTOA_FORMAT_FRAC) {
+ int len;
+
+ njs_assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS);
+ /* P = max_int(E, 1) + n_digits; */
+ /* frac is rounded using RNDNA */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA);
+
+ /* we add one extra digit on the left and remove it if needed
+ to avoid testing if the result is < radix^P */
+ len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits,
+ max_int(E + 1, 1));
+ if (q[0] == '0' && len >= 2 && q[1] != '.') {
+ len--;
+ memmove(q, q + 1, len);
+ }
+ q += len;
+ goto done;
+ } else {
+ int pow_shift;
+ njs_assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS);
+ P = n_digits;
+ /* mant_max = radix^P */
+ mant_max->len = 1;
+ mant_max->tab[0] = 1;
+ pow_shift = mul_pow(mant_max, radix1, radix_shift, P, FALSE, 0);
+ mpb_shr_round(mant_max, pow_shift, JS_RNDZ);
+
+ for(;;) {
+ /* fixed and frac are rounded using RNDNA */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA);
+ if (mpb_cmp(tmp1, mant_max) < 0)
+ break;
+ E++; /* at most one iteration is possible */
+ }
+ }
+ output:
+ if (fmt == JS_DTOA_FORMAT_FIXED)
+ E_max = n_digits;
+ else
+ E_max = dtoa_max_digits_table[radix - 2] + 4;
+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED ||
+ ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) {
+ q += output_digits(q, tmp1, radix, P, 1);
+ E--;
+ if (radix == 10) {
+ *q++ = 'e';
+ } else if (radix1 == 1 && radix_shift <= 4) {
+ E *= radix_shift;
+ *q++ = 'p';
+ } else {
+ *q++ = '@';
+ }
+ if (E < 0) {
+ *q++ = '-';
+ E = -E;
+ } else {
+ *q++ = '+';
+ }
+ q += njs_u32toa(q, E);
+ } else if (E <= 0) {
+ *q++ = '0';
+ *q++ = '.';
+ for(i = 0; i < -E; i++)
+ *q++ = '0';
+ q += output_digits(q, tmp1, radix, P, P);
+ } else {
+ q += output_digits(q, tmp1, radix, P, min_int(P, E));
+ for(i = 0; i < E - P; i++)
+ *q++ = '0';
+ }
+ done:
+ dtoa_free(mant_max);
+ dtoa_free(tmp1);
+ return q - buf;
+}
- if (frac == -1) {
- length = njs_grisu2(value, p, &point);
+static inline int to_digit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ else
+ return 36;
+}
+/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */
+static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a)
+{
+ int i;
+ if (r->tab[0] == 0 && r->len == 1) {
+ r->tab[0] = a;
+ } else {
+ if (radix_base == 0) {
+ for(i = r->len; i >= 0; i--) {
+ r->tab[i + 1] = r->tab[i];
+ }
+ r->tab[0] = a;
} else {
- length = njs_grisu2_prec(value, p, frac + 1, &point);
+ r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len,
+ radix_base, a);
}
+ r->len++;
+ mpb_renorm(r);
+ }
+}
+/* XXX: add fast path for small integers */
+double njs_atod2(const char *str, const char **pnext, int radix, int flags,
+ JSATODTempMem *tmp_mem)
+{
+ uint64_t *mptr = tmp_mem->mem;
+ const char *p, *p_start;
+ limb_t cur_limb, radix_base, extra_digits;
+ int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift;
+ int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos;
+ mpb_t *tmp0;
+ double dval;
+ BOOL is_bin_exp, is_zero, expn_overflow;
+ uint64_t m, a;
+
+ tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);
+ njs_assert((size_t) (mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0]));
+ /* optional separator between digits */
+ sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+
+ p = str;
+ is_neg = 0;
+ if (p[0] == '+') {
+ p++;
+ p_start = p;
+ if (!(flags & JS_ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ } else if (p[0] == '-') {
+ is_neg = 1;
+ p++;
+ p_start = p;
+ if (!(flags & JS_ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
} else {
- start[0] = '0';
- length = 1;
- point = 1;
+ p_start = p;
}
-
- if (frac == -1) {
- frac = length - 1;
+
+ if (p[0] == '0') {
+ if ((p[1] == 'x' || p[1] == 'X') &&
+ (radix == 0 || radix == 16)) {
+ p += 2;
+ radix = 16;
+ } else if ((p[1] == 'o' || p[1] == 'O') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 8;
+ } else if ((p[1] == 'b' || p[1] == 'B') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 2;
+ } else if ((p[1] >= '0' && p[1] <= '9') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) {
+ int i;
+ sep = 256;
+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+ continue;
+ if (p[i] == '8' || p[i] == '9')
+ goto no_prefix;
+ p += 1;
+ radix = 8;
+ } else {
+ goto no_prefix;
+ }
+ /* there must be a digit after the prefix */
+ if (to_digit((uint8_t)*p) >= radix)
+ goto fail;
+ no_prefix: ;
+ } else {
+ no_radix_prefix:
+ if (!(flags & JS_ATOD_INT_ONLY) && strstart(p, "Infinity", &p))
+ goto overflow;
+ }
+ if (radix == 0)
+ radix = 10;
+
+ cur_limb = 0;
+ expn_offset = 0;
+ digit_count = 0;
+ limb_digit_count = 0;
+ max_digits = atod_max_digits_table[radix - 2];
+ digits_per_limb = digits_per_limb_table[radix - 2];
+ radix_base = radix_base_table[radix - 2];
+ radix_shift = ctz32(radix);
+ radix1 = radix >> radix_shift;
+ if (radix1 == 1) {
+ /* radix = 2^radix_bits */
+ radix_bits = radix_shift;
+ } else {
+ radix_bits = 0;
+ }
+ tmp0->len = 1;
+ tmp0->tab[0] = 0;
+ extra_digits = 0;
+ pos = 0;
+ dot_pos = -1;
+ /* skip leading zeros */
+ for(;;) {
+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&
+ !(flags & JS_ATOD_INT_ONLY)) {
+ if (*p == sep)
+ goto fail;
+ if (dot_pos >= 0)
+ break;
+ dot_pos = pos;
+ p++;
+ }
+ if (*p == sep && p > p_start && p[1] == '0')
+ p++;
+ if (*p != '0')
+ break;
+ p++;
+ pos++;
+ }
+
+ sig_pos = pos;
+ for(;;) {
+ limb_t c;
+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&
+ !(flags & JS_ATOD_INT_ONLY)) {
+ if (*p == sep)
+ goto fail;
+ if (dot_pos >= 0)
+ break;
+ dot_pos = pos;
+ p++;
+ }
+ if (*p == sep && p > p_start && to_digit(p[1]) < radix)
+ p++;
+ c = to_digit(*p);
+ if ((int) c >= radix)
+ break;
+ p++;
+ pos++;
+ if (digit_count < max_digits) {
+ /* XXX: could be faster when radix_bits != 0 */
+ cur_limb = cur_limb * radix + c;
+ limb_digit_count++;
+ if (limb_digit_count == digits_per_limb) {
+ mpb_mul1_base(tmp0, radix_base, cur_limb);
+ cur_limb = 0;
+ limb_digit_count = 0;
+ }
+ digit_count++;
+ } else {
+ extra_digits |= c;
+ }
+ }
+ if (limb_digit_count != 0) {
+ mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb);
+ }
+ if (digit_count == 0) {
+ is_zero = TRUE;
+ expn_offset = 0;
+ } else {
+ is_zero = FALSE;
+ if (dot_pos < 0)
+ dot_pos = pos;
+ expn_offset = sig_pos + digit_count - dot_pos;
+ }
+
+ /* Use the extra digits for rounding if the base is a power of
+ two. Otherwise they are just truncated. */
+ if (radix_bits != 0 && extra_digits != 0) {
+ tmp0->tab[0] |= 1;
+ }
+
+ /* parse the exponent, if any */
+ expn = 0;
+ expn_overflow = FALSE;
+ is_bin_exp = FALSE;
+ if (!(flags & JS_ATOD_INT_ONLY) &&
+ ((radix == 10 && (*p == 'e' || *p == 'E')) ||
+ (radix != 10 && (*p == '@' ||
+ (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) &&
+ p > p_start) {
+ BOOL exp_is_neg;
+ int c;
+ const char *p1;
+ p1 = p;
+ is_bin_exp = (*p1 == 'p' || *p1 == 'P');
+ p1++;
+ exp_is_neg = 0;
+ if (*p1 == '+') {
+ p1++;
+ } else if (*p1 == '-') {
+ exp_is_neg = 1;
+ p1++;
+ }
+ c = to_digit(*p1);
+ if (c >= 10)
+ goto skip_exp;
+ expn = c;
+ p1++;
+ for(;;) {
+ if (*p1 == sep && to_digit(p1[1]) < 10)
+ p1++;
+ c = to_digit(*p1);
+ if (c >= 10)
+ break;
+ if (!expn_overflow) {
+ if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) {
+ expn_overflow = TRUE;
+ } else {
+ expn = expn * 10 + c;
+ }
+ }
+ p1++;
+ }
+ p = p1;
+ if (exp_is_neg)
+ expn = -expn;
+ /* if zero result, the exponent can be arbitrarily large */
+ if (!is_zero && expn_overflow) {
+ if (exp_is_neg)
+ a = 0;
+ else
+ a = (uint64_t)0x7ff << 52; /* infinity */
+ goto done;
+ }
}
- return njs_dtoa_exp_format(p, point - 1, frac + 1, length) + minus;
+skip_exp:
+ if (p == p_start)
+ goto fail;
+
+ if (is_zero) {
+ a = 0;
+ } else {
+ int expn1;
+ if (radix_bits != 0) {
+ if (!is_bin_exp)
+ expn *= radix_bits;
+ expn -= expn_offset * radix_bits;
+ expn1 = expn + digit_count * radix_bits;
+ if (expn1 >= 1024 + radix_bits)
+ goto overflow;
+ else if (expn1 <= -1075)
+ goto underflow;
+ m = round_to_d(&e, tmp0, -expn, JS_RNDN);
+ } else {
+ expn -= expn_offset;
+ expn1 = expn + digit_count;
+ if (expn1 >= max_exponent[radix - 2] + 1)
+ goto overflow;
+ else if (expn1 <= min_exponent[radix - 2])
+ goto underflow;
+ m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN);
+ }
+ if (m == 0) {
+ underflow:
+ a = 0;
+ } else if (e > 1024) {
+ overflow:
+ /* overflow */
+ a = (uint64_t)0x7ff << 52;
+ } else if (e < -1073) {
+ /* underflow */
+ /* XXX: check rounding */
+ a = 0;
+ } else if (e < -1021) {
+ /* subnormal */
+ a = m >> (-e - 1021);
+ } else {
+ a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1));
+ }
+ }
+ done:
+ a |= (uint64_t)is_neg << 63;
+ dval = uint64_as_float64(a);
+ done1:
+ if (pnext)
+ *pnext = p;
+ dtoa_free(tmp0);
+ return dval;
+ fail:
+ dval = NAN;
+ goto done1;
}
-
/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) Nginx, Inc.
+ * Tiny float64 printing and parsing library
+ *
+ * Copyright (c) 2024 Fabrice Bellard
+ *
+ * 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.
*/
#ifndef _NJS_DTOA_H_INCLUDED_
#define _NJS_DTOA_H_INCLUDED_
-#define NJS_DTOA_MAX_LEN njs_length("-1.7976931348623157e+308")
+#include <njs_types.h>
+#include <njs_clang.h>
+
+//#define JS_DTOA_DUMP_STATS
+
+/* maximum number of digits for fixed and frac formats */
+#define JS_DTOA_MAX_DIGITS 101
+
+/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */
+/* use as many digits as necessary */
+#define JS_DTOA_FORMAT_FREE (0 << 0)
+/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */
+#define JS_DTOA_FORMAT_FIXED (1 << 0)
+/* force fractional format: [-]dd.dd with n_digits fractional digits.
+ 0 <= n_digits <= JS_DTOA_MAX_DIGITS */
+#define JS_DTOA_FORMAT_FRAC (2 << 0)
+#define JS_DTOA_FORMAT_MASK (3 << 0)
+
+/* select exponential notation either in fixed or free format */
+#define JS_DTOA_EXP_AUTO (0 << 2)
+#define JS_DTOA_EXP_ENABLED (1 << 2)
+#define JS_DTOA_EXP_DISABLED (2 << 2)
+#define JS_DTOA_EXP_MASK (3 << 2)
+
+#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */
+
+/* only accepts integers (no dot, no exponent) */
+#define JS_ATOD_INT_ONLY (1 << 0)
+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
+#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1)
+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
+#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2)
+/* accept _ between digits as a digit separator */
+#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3)
+/* accept radix prefix (0x, 0o, 0b) after sign (+/-) */
+#define JS_ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 4)
+
+typedef struct {
+ uint64_t mem[37];
+} JSDTOATempMem;
+
+typedef struct {
+ uint64_t mem[27];
+} JSATODTempMem;
+
+/* return a maximum bound of the string length */
+int njs_dtoa_max_len(double d, int radix, int n_digits, int flags);
+/* return the string length */
+int njs_dtoa2(char *buf, double d, int radix, int n_digits, int flags,
+ JSDTOATempMem *tmp_mem);
+double njs_atod2(const char *str, const char **pnext, int radix, int flags,
+ JSATODTempMem *tmp_mem);
+
+#ifdef JS_DTOA_DUMP_STATS
+void njs_dtoa_dump_stats(void);
+#endif
+
+/* additional exported functions */
+size_t njs_u32toa(char *buf, uint32_t n);
+size_t njs_i32toa(char *buf, int32_t n);
+size_t njs_u64toa(char *buf, uint64_t n);
+size_t njs_i64toa(char *buf, int64_t n);
+size_t njs_u64toa_radix(char *buf, uint64_t n, unsigned int radix);
+size_t njs_i64toa_radix(char *buf, int64_t n, unsigned int radix);
+
+
+njs_inline size_t
+njs_dtoa(double value, char *start)
+{
+ JSDTOATempMem tmp_mem;
+
+ return njs_dtoa2(start, value, 10, 0, JS_DTOA_FORMAT_FREE, &tmp_mem);
+}
+
+/* Expects a null-terminated string. */
+njs_inline double
+njs_atod(const char *str, const char **pnext, int radix, int flags)
+{
+ JSATODTempMem tmp_mem;
-NJS_EXPORT size_t njs_dtoa(double value, char *start);
-NJS_EXPORT size_t njs_dtoa_precision(double value, char *start, size_t prec);
-NJS_EXPORT size_t njs_dtoa_exponential(double value, char *start,
- njs_int_t frac);
+ return njs_atod2(str, pnext, radix, flags, &tmp_mem);
+}
#endif /* _NJS_DTOA_H_INCLUDED_ */
+++ /dev/null
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- *
- * An internal fixed_dtoa() implementation based upon V8
- * src/numbers/fixed-dtoa.cc without bignum support.
- *
- * Copyright 2011 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 <njs_main.h>
-#include <njs_diyfp.h>
-
-
-typedef struct {
- uint64_t high;
- uint64_t low;
-} njs_diyu128_t;
-
-
-#define njs_diyu128(_h, _l) (njs_diyu128_t) { .high = (_h), .low = (_l) }
-
-
-njs_inline njs_diyu128_t
-njs_diyu128_mul(njs_diyu128_t v, uint32_t multiplicand)
-{
- uint32_t part;
- uint64_t accumulator;
-
- accumulator = (v.low & UINT32_MAX) * multiplicand;
- part = (uint32_t) (accumulator & UINT32_MAX);
-
- accumulator >>= 32;
- accumulator = accumulator + (v.low >> 32) * multiplicand;
- v.low = (accumulator << 32) + part;
-
- accumulator >>= 32;
- accumulator = accumulator + (v.high & UINT32_MAX) * multiplicand;
- part = (uint32_t) (accumulator & UINT32_MAX);
-
- accumulator >>= 32;
- accumulator = accumulator + (v.high >> 32) * multiplicand;
- v.high = (accumulator << 32) + part;
-
- return v;
-}
-
-
-njs_inline njs_diyu128_t
-njs_diyu128_shift(njs_diyu128_t v, njs_int_t shift)
-{
- /* -64 <= shift <= 64.*/
-
- if (shift < 0) {
- if (shift == -64) {
- v.high = v.low;
- v.low = 0;
-
- } else {
- v.high <<= -shift;
- v.high += v.low >> (64 + shift);
- v.low <<= -shift;
- }
-
- return v;
- }
-
- if (shift > 0) {
- if (shift == 64) {
- v.low = v.high;
- v.high = 0;
-
- } else {
- v.low >>= shift;
- v.low += v.high << (64 - shift);
- v.high >>= shift;
- }
- }
-
- return v;
-}
-
-
-njs_inline njs_int_t
-njs_diyu128_div_mod_pow2(njs_diyu128_t *v, njs_int_t power)
-{
- uint64_t part_low, part_high;
- njs_int_t result;
-
- if (power >= 64) {
- result = (int) (v->high >> (power - 64));
- v->high -= (uint64_t) result << (power - 64);
-
- return result;
- }
-
- part_low = v->low >> power;
- part_high = v->high << (64 - power);
-
- result = (int) (part_low + part_high);
-
- v->low -= part_low << power;
- v->high = 0;
-
- return result;
-}
-
-
-njs_inline njs_bool_t
-njs_diyu128_is_zero(njs_diyu128_t v)
-{
- if (v.low == 0 && v.high == 0) {
- return 1;
- }
-
- return 0;
-}
-
-
-njs_inline njs_uint_t
-njs_diyu128_bit_at(njs_diyu128_t v, njs_uint_t pos)
-{
- if (pos >= 64) {
- return (njs_uint_t) (v.high >> (pos - 64)) & 1;
- }
-
- return (njs_uint_t) (v.low >> pos) & 1;
-}
-
-
-static size_t
-njs_fill_digits32(uint32_t number, char *start, size_t length)
-{
- char c;
- size_t i, j, n;
- njs_int_t digit;
-
- n = 0;
-
- while (number != 0) {
- digit = number % 10;
- number /= 10;
- start[length + n] = '0' + digit;
- n++;
- }
-
- i = length;
- j = length + n - 1;
-
- while (i < j) {
- c = start[i];
- start[i] = start[j];
- start[j] = c;
-
- i++;
- j--;
- }
-
- return length + n;
-}
-
-
-njs_inline size_t
-njs_fill_digits32_fixed_length(uint32_t number, size_t requested_length,
- char *start, size_t length)
-{
- size_t i;
-
- i = requested_length;
-
- while (i-- > 0) {
- start[length + i] = '0' + number % 10;
- number /= 10;
- }
-
- return length + requested_length;
-}
-
-
-njs_inline size_t
-njs_fill_digits64(uint64_t number, char *start, size_t length)
-{
- uint32_t part0, part1, part2, ten7;
-
- ten7 = 10000000;
-
- part2 = (uint32_t) (number % ten7);
- number /= ten7;
-
- part1 = (uint32_t) (number % ten7);
- part0 = (uint32_t) (number / ten7);
-
- if (part0 != 0) {
- length = njs_fill_digits32(part0, start, length);
- length = njs_fill_digits32_fixed_length(part1, 7, start, length);
- return njs_fill_digits32_fixed_length(part2, 7, start, length);
- }
-
- if (part1 != 0) {
- length = njs_fill_digits32(part1, start, length);
- return njs_fill_digits32_fixed_length(part2, 7, start, length);
- }
-
- return njs_fill_digits32(part2, start, length);
-}
-
-
-njs_inline size_t
-njs_fill_digits64_fixed_length(uint64_t number, char *start, size_t length)
-{
- uint32_t part0, part1, part2, ten7;
-
- ten7 = 10000000;
-
- part2 = (uint32_t) (number % ten7);
- number /= ten7;
-
- part1 = (uint32_t) (number % ten7);
- part0 = (uint32_t) (number / ten7);
-
- length = njs_fill_digits32_fixed_length(part0, 3, start, length);
- length = njs_fill_digits32_fixed_length(part1, 7, start, length);
-
- return njs_fill_digits32_fixed_length(part2, 7, start, length);
-}
-
-
-njs_inline size_t
-njs_dtoa_round_up(char *start, size_t length, njs_int_t *point)
-{
- size_t i;
-
- if (length == 0) {
- start[0] = '1';
- *point = 1;
- return 1;
- }
-
- start[length - 1]++;
-
- for (i = length - 1; i > 0; --i) {
- if (start[i] != '0' + 10) {
- return length;
- }
-
- start[i] = '0';
- start[i - 1]++;
- }
-
- if (start[0] == '0' + 10) {
- start[0] = '1';
- (*point)++;
- }
-
- return length;
-}
-
-
-static size_t
-njs_fill_fractionals(uint64_t fractionals, int exponent, njs_uint_t frac,
- char *start, size_t length, njs_int_t *point)
-{
- njs_int_t n, digit;
- njs_uint_t i;
- njs_diyu128_t fractionals128;
-
- /*
- * -128 <= exponent <= 0.
- * 0 <= fractionals * 2^exponent < 1.
- */
-
- if (-exponent <= 64) {
- /* fractionals <= 2^56. */
-
- n = -exponent;
-
- for (i = 0; i < frac && fractionals != 0; ++i) {
- /*
- * Multiplication by 10 is replaced with multiplication by 5 and
- * point location adjustment. To avoid integer-overflow.
- */
-
- fractionals *= 5;
- n--;
-
- digit = (njs_int_t) (fractionals >> n);
- fractionals -= (uint64_t) digit << n;
-
- start[length++] = '0' + digit;
- }
-
- if (n > 0 && ((fractionals >> (n - 1)) & 1)) {
- length = njs_dtoa_round_up(start, length, point);
- }
-
- } else {
-
- fractionals128 = njs_diyu128(fractionals, 0);
- fractionals128 = njs_diyu128_shift(fractionals128, -exponent - 64);
-
- n = 128;
-
- for (i = 0; i < frac && !njs_diyu128_is_zero(fractionals128); ++i) {
- /*
- * Multiplication by 10 is replaced with multiplication by 5 and
- * point location adjustment. To avoid integer-overflow.
- */
-
- fractionals128 = njs_diyu128_mul(fractionals128, 5);
- n--;
-
- digit = njs_diyu128_div_mod_pow2(&fractionals128, n);
-
- start[length++] = '0' + digit;
- }
-
- if (njs_diyu128_bit_at(fractionals128, n - 1)) {
- length = njs_dtoa_round_up(start, length, point);
- }
- }
-
- return length;
-}
-
-
-njs_inline size_t
-njs_trim_zeroes(char *start, size_t length, njs_int_t *point)
-{
- size_t i, n;
-
- while (length > 0 && start[length - 1] == '0') {
- length--;
- }
-
- n = 0;
-
- while (n < length && start[n] == '0') {
- n++;
- }
-
- if (n != 0) {
- for (i = n; i < length; ++i) {
- start[i - n] = start[i];
- }
-
- length -= n;
- *point -= n;
- }
-
- return length;
-}
-
-
-size_t
-njs_fixed_dtoa(double value, njs_uint_t frac, char *start, njs_int_t *point)
-{
- size_t length;
- uint32_t quotient;
- uint64_t significand, divisor, dividend, remainder, integral, fract;
- njs_int_t exponent;
- njs_diyfp_t v;
-
- length = 0;
- v = njs_d2diyfp(value);
-
- significand = v.significand;
- exponent = v.exp;
-
- /* exponent <= 19. */
-
- if (exponent + NJS_SIGNIFICAND_SIZE > 64) {
- /* exponent > 11. */
-
- divisor = njs_uint64(0xB1, 0xA2BC2EC5); /* 5 ^ 17 */
-
- dividend = significand;
-
- if (exponent > 17) {
- /* (e - 17) <= 3. */
- dividend <<= exponent - 17;
-
- quotient = (uint32_t) (dividend / divisor);
- remainder = (dividend % divisor) << 17;
-
- } else {
- divisor <<= 17 - exponent;
-
- quotient = (uint32_t) (dividend / divisor);
- remainder = (dividend % divisor) << exponent;
- }
-
- length = njs_fill_digits32(quotient, start, length);
- length = njs_fill_digits64_fixed_length(remainder, start, length);
- *point = (njs_int_t) length;
-
- } else if (exponent >= 0) {
- /* 0 <= exponent <= 11. */
-
- significand <<= exponent;
- length = njs_fill_digits64(significand, start, length);
- *point = (njs_int_t) length;
-
- } else if (exponent > -NJS_SIGNIFICAND_SIZE) {
- /* -53 < exponent < 0. */
-
- integral = significand >> -exponent;
- fract = significand - (integral << -exponent);
-
- if (integral > UINT32_MAX) {
- length = njs_fill_digits64(integral, start, length);
-
- } else {
- length = njs_fill_digits32((uint32_t) integral, start, length);
- }
-
- *point = (njs_int_t) length;
- length = njs_fill_fractionals(fract, exponent, frac, start, length,
- point);
-
- } else if (exponent < -128) {
- /* Valid for frac =< 20 only. TODO: bignum support. */
-
- start[0] = '\0';
-
- *point = -frac;
-
- } else {
- /* -128 <= exponent <= -53. */
-
- *point = 0;
- length = njs_fill_fractionals(significand, exponent, frac, start,
- length, point);
- }
-
- length = njs_trim_zeroes(start, length, point);
- start[length] = '\0';
-
- if (length == 0) {
- *point = -frac;
- }
-
- return length;
-}
+++ /dev/null
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) Nginx, Inc.
- */
-
-#ifndef _NJS_DTOA_FIXED_H_INCLUDED_
-#define _NJS_DTOA_FIXED_H_INCLUDED_
-
-NJS_EXPORT size_t njs_fixed_dtoa(double value, njs_uint_t frac, char *start,
- njs_int_t *point);
-
-#endif /* _NJS_DTOA_FIXED_H_INCLUDED_ */
njs_json_parse_number(njs_json_parse_ctx_t *ctx, njs_value_t *value,
const u_char *p)
{
- double num;
- njs_int_t sign;
- const u_char *start;
+ double num;
+ const char *start, *end;
- sign = 1;
+ start = (const char *) p;
+ num = njs_atod((const char *) p, &end, 10, 0);
- if (*p == '-') {
- if (p + 1 == ctx->end) {
- goto error;
- }
-
- p++;
- sign = -1;
- }
-
- start = p;
- num = njs_number_dec_parse(&p, ctx->end, 0);
- if (p != start) {
- njs_set_number(value, sign * num);
- return p;
+ if (end != start && !isnan(num)) {
+ njs_set_number(value, num);
+ return (const u_char *) end;
}
-error:
-
njs_json_parse_exception(ctx, "Unexpected number", p);
return NULL;
static void
njs_lexer_number(njs_lexer_t *lexer, njs_lexer_token_t *token)
{
- u_char c;
+ int flags;
const u_char *p;
- c = lexer->start[-1];
- p = lexer->start;
-
token->text.start = lexer->start - 1;
+ p = token->text.start;
- if (c == '0' && p != lexer->end) {
+ flags = JS_ATOD_ACCEPT_BIN_OCT | JS_ATOD_ACCEPT_UNDERSCORES;
- /* Hexadecimal literal values. */
+ if (p[0] == '0' && (p + 1) < lexer->end) {
+ /* Legacy octal literal. */
- if (*p == 'x' || *p == 'X') {
- p++;
+ if (p[1] == '_' || (p[1] >= '0' && p[1] <= '9')) {
+ for (p++; p < lexer->end; p++) {
+ if (*p == '_' || (*p >= '0' && *p <= '9')) {
+ continue;
+ }
- if (p == lexer->end || njs_char_to_hex(*p) < 0) {
- goto illegal_token;
+ break;
}
- token->number = njs_number_hex_parse(&p, lexer->end, 1);
-
- goto done;
+ goto illegal_token;
}
- /* Octal literal values. */
-
- if (*p == 'o' || *p == 'O') {
- p++;
-
- if (p == lexer->end || (u_char)(*p - '0') > 7) {
- goto illegal_token;
- }
-
- token->number = njs_number_oct_parse(&p, lexer->end, 1);
-
- if (p < lexer->end && (*p == '8' || *p == '9')) {
- goto illegal_trailer;
- }
-
- goto done;
+ if ((p[1] == 'x' || p[1] == 'X'
+ || p[1] == 'b' || p[1] == 'B'
+ || p[1] == 'o' || p[1] == 'O'))
+ {
+ flags |= JS_ATOD_INT_ONLY;
}
+ }
- /* Binary literal values. */
-
- if (*p == 'b' || *p == 'B') {
- p++;
-
- if (p == lexer->end || (u_char)(*p - '0') > 1) {
- goto illegal_token;
- }
+ token->number = njs_atod((const char *) p, (const char **) &p, 0, flags);
- token->number = njs_number_bin_parse(&p, lexer->end, 1);
+#define njs_continuation_char(c) \
+ (njs_tokens[c] == NJS_TOKEN_LETTER || njs_tokens[c] == NJS_TOKEN_DIGIT)
- if (p < lexer->end && (*p >= '2' && *p <= '9')) {
- goto illegal_trailer;
+ if (isnan(token->number) || (p < lexer->end && njs_continuation_char(*p))) {
+ while (p < lexer->end) {
+ if (!njs_continuation_char(*p)) {
+ break;
}
- goto done;
- }
-
- /* Legacy Octal literals are deprecated. */
-
- if ((*p >= '0' && *p <= '9') || *p == '_') {
- goto illegal_trailer;
+ p++;
}
- }
- p--;
- token->number = njs_number_dec_parse(&p, lexer->end, 1);
-
-done:
-
- if (p[-1] == '_') {
- p--;
+ goto illegal_token;
}
lexer->start = (u_char *) p;
return;
-illegal_trailer:
-
- p++;
-
illegal_token:
token->text.length = p - token->text.start;
#include <njs_unicode.h>
#include <njs_utf8.h>
#include <njs_utf16.h>
-#include <njs_diyfp.h>
#include <njs_dtoa.h>
-#include <njs_dtoa_fixed.h>
-#include <njs_strtod.h>
#include <njs_djb_hash.h>
#include <njs_murmur_hash.h>
#include <njs_trace.h>
}
-double
-njs_number_dec_parse(const u_char **start, const u_char *end,
- njs_bool_t literal)
-{
- return njs_strtod(start, end, literal);
-}
-
-
-double
-njs_number_oct_parse(const u_char **start, const u_char *end,
- njs_bool_t literal)
-{
- u_char c;
- double num;
- const u_char *p, *_;
-
- p = *start;
-
- num = 0;
- _ = p - 1;
-
- for (; p < end; p++) {
- /* Values less than '0' become >= 208. */
- c = *p - '0';
-
- if (njs_slow_path(c > 7)) {
- if (literal && *p == '_' && (p - _) > 1) {
- _ = p;
- continue;
- }
-
- break;
- }
-
- num = num * 8 + c;
- }
-
- *start = p;
-
- return num;
-}
-
-
-double
-njs_number_bin_parse(const u_char **start, const u_char *end,
- njs_bool_t literal)
-{
- u_char c;
- double num;
- const u_char *p, *_;
-
- p = *start;
-
- num = 0;
- _ = p - 1;
-
- for (; p < end; p++) {
- /* Values less than '0' become >= 208. */
- c = *p - '0';
-
- if (njs_slow_path(c > 1)) {
- if (literal && *p == '_' && (p - _) > 1) {
- _ = p;
- continue;
- }
-
- break;
- }
-
- num = num * 2 + c;
- }
-
- *start = p;
-
- return num;
-}
-
-
double
njs_number_hex_parse(const u_char **start, const u_char *end,
njs_bool_t literal)
}
-static double
-njs_number_radix_parse(const u_char **start, const u_char *end, uint8_t radix)
-{
- uint8_t d;
- double num, n;
- const u_char *p;
-
- static const int8_t digits[256]
- njs_aligned(32) =
- {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- };
-
- num = NAN;
- n = 0;
-
- for (p = *start; p < end; p++) {
- d = digits[*p];
-
- if (njs_slow_path(d >= radix)) {
- break;
- }
-
- n = (n * radix) + d;
- num = n;
- }
-
- *start = p;
-
- return num;
-}
-
-
njs_int_t
njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
const njs_value_t *number)
njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
{
- u_char *p;
- int64_t frac;
- double number;
- size_t length, size;
- njs_int_t ret, point, prefix, postfix;
- njs_value_t *value;
- u_char buf[128], buf2[128];
+ double number;
+ size_t size;
+ int64_t frac;
+ njs_int_t ret;
+ njs_value_t *value;
+ JSDTOATempMem tmp_mem;
+ u_char buf[128];
/* 128 > 100 + 21 + njs_length(".-\0"). */
return njs_number_to_string(vm, retval, value);
}
- point = 0;
- length = njs_fixed_dtoa(number, (njs_int_t) frac, (char *) buf, &point);
-
- prefix = 0;
- postfix = 0;
-
- if (point <= 0) {
- prefix = -point + 1;
- point = 1;
- }
-
- if (prefix + (njs_int_t) length < point + frac) {
- postfix = point + frac - length - prefix;
- }
-
- size = prefix + length + postfix + !!(number < 0);
-
- if (frac > 0) {
- size += njs_length(".");
- }
-
- p = buf2;
-
- while (--prefix >= 0) {
- *p++ = '0';
- }
-
- if (length != 0) {
- p = njs_cpymem(p, buf, length);
- }
-
- while (--postfix >= 0) {
- *p++ = '0';
- }
+ size = njs_dtoa2((char *) buf, number, 10, (int) frac,
+ JS_DTOA_FORMAT_FRAC, &tmp_mem);
- p = njs_string_alloc(vm, retval, size, size);
- if (njs_slow_path(p == NULL)) {
- return NJS_ERROR;
- }
-
- if (number < 0) {
- *p++ = '-';
- }
-
- p = njs_cpymem(p, buf2, point);
-
- if (frac > 0) {
- *p++ = '.';
-
- memcpy(p, &buf2[point], frac);
- }
-
- return NJS_OK;
+ return njs_string_new(vm, retval, buf, size, size);
}
njs_number_prototype_to_precision(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
{
- double number;
- size_t size;
- int64_t precision;
- njs_int_t ret;
- njs_value_t *value;
- u_char buf[128];
+ double number;
+ size_t size;
+ int64_t precision;
+ njs_int_t ret;
+ njs_value_t *value;
+ JSDTOATempMem tmp_mem;
+ u_char buf[128];
/* 128 > 100 + 21 + njs_length(".-\0"). */
return NJS_ERROR;
}
- size = njs_dtoa_precision(number, (char *) buf, (size_t) precision);
+ size = njs_dtoa2((char *) buf, number, 10, (int) precision,
+ JS_DTOA_FORMAT_FIXED, &tmp_mem);
return njs_string_new(vm, retval, buf, size, size);
}
njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
{
- double number;
- size_t size;
- int64_t frac;
- njs_int_t ret;
- njs_value_t *value, *value_frac;
- u_char buf[128];
+ int digits, flags;
+ double number;
+ size_t size;
+ int64_t frac;
+ njs_int_t ret;
+ njs_value_t *value, *value_frac;
+ JSDTOATempMem tmp_mem;
+ u_char buf[128];
value = &args[0];
frac = -1;
}
- size = njs_dtoa_exponential(number, (char *) buf, (njs_int_t) frac);
+ if (frac < 0) {
+ digits = 0;
+ flags = JS_DTOA_FORMAT_FREE;
+
+ } else {
+ digits = frac + 1;
+ flags = JS_DTOA_FORMAT_FIXED;
+ }
+
+ size = njs_dtoa2((char *) buf, number, 10, digits,
+ flags | JS_DTOA_EXP_ENABLED, &tmp_mem);
return njs_string_new(vm, retval, buf, size, size);
}
/*
- * The radix equal to 2 produces the longest value for a number.
+ * njs_dtoa_max_len() caps radix conversions (format free, no exponent) at 1088
+ * characters plus the terminating null, so a fixed 1.1KB stack buffer is safe.
*/
-
-#define NJS_STRING_RADIX_INTERGRAL_LEN (1 + 1024)
-#define NJS_STRING_RADIX_FRACTION_LEN (1 + 1075)
-#define NJS_STRING_RADIX_LEN \
- (NJS_STRING_RADIX_INTERGRAL_LEN + NJS_STRING_RADIX_FRACTION_LEN)
-
-
-njs_inline double
-njs_number_next_double(double n)
-{
- njs_diyfp_t v;
-
- v = njs_d2diyfp(n);
-
- if (signbit(n)) {
- if (v.significand == 0) {
- return 0.0;
- }
-
- v.significand--;
-
- } else {
- v.significand++;
- }
-
- return njs_diyfp2d(v);
-}
-
+#define NJS_NUMBER_RADIX_BUF_SIZE 1100
static njs_int_t
njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string,
double number, uint32_t radix)
{
- int digit;
- char ch;
- double n, remainder, integer, fraction, delta;
- u_char *p, *end;
- uint32_t size;
- u_char buf[NJS_STRING_RADIX_LEN];
-
- static const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
-
- p = buf + NJS_STRING_RADIX_INTERGRAL_LEN;
- end = p;
-
- n = number;
-
- if (n < 0) {
- n = -n;
- }
-
- integer = floor(n);
- fraction = n - integer;
-
- delta = 0.5 * (njs_number_next_double(n) - n);
- delta = njs_max(njs_number_next_double(0.0), delta);
-
- if (fraction >= delta && delta != 0) {
- *p++ = '.';
-
- do {
- fraction *= radix;
- delta *= radix;
-
- digit = (int) fraction;
- *p++ = digits[digit];
-
- fraction -= digit;
-
- if ((fraction > 0.5 || (fraction == 0.5 && (digit & 1)))
- && (fraction + delta > 1))
- {
- while (p-- != buf) {
- if (p == buf + NJS_STRING_RADIX_INTERGRAL_LEN) {
- integer += 1;
- break;
- }
-
- ch = *p;
- digit = (ch > '9') ? ch - 'a' + 10 : ch - '0';
-
- if ((uint32_t) (digit + 1) < radix) {
- *p++ = digits[digit + 1];
- break;
- }
- }
-
- break;
- }
-
- } while (fraction >= delta);
-
- end = p;
- }
-
- p = buf + NJS_STRING_RADIX_INTERGRAL_LEN;
-
- while (njs_d2diyfp(integer / radix).exp > 0) {
- integer /= radix;
- *(--p) = '0';
+ int len;
+ size_t size;
+ u_char buf[NJS_NUMBER_RADIX_BUF_SIZE];
+ njs_int_t ret;
+ JSDTOATempMem tmp_mem;
+
+ len = njs_dtoa_max_len(number, (int) radix, 0,
+ JS_DTOA_FORMAT_FREE | JS_DTOA_EXP_DISABLED);
+ if (njs_slow_path((size_t) len + 1 > NJS_NUMBER_RADIX_BUF_SIZE)) {
+ njs_internal_error(vm, "radix buffer overflow");
+ return NJS_ERROR;
}
- do {
- remainder = fmod(integer, radix);
- *(--p) = digits[(int) remainder];
- integer = (integer - remainder) / radix;
-
- } while (integer > 0);
-
- if (number < 0) {
- *(--p) = '-';
- }
+ size = njs_dtoa2((char *) buf, number, (int) radix, 0,
+ JS_DTOA_FORMAT_FREE | JS_DTOA_EXP_DISABLED, &tmp_mem);
- size = (uint32_t) (end - p);
+ ret = njs_string_new(vm, string, buf, (uint32_t) size, (uint32_t) size);
- return njs_string_new(vm, string, p, size, size);
+ return ret;
}
double num;
int32_t radix;
njs_int_t ret;
- njs_bool_t minus, test_prefix;
njs_value_t *value, lvalue;
- const u_char *p, *end;
njs_string_prop_t string;
num = NAN;
-
+ radix = 0;
value = njs_lvalue_arg(&lvalue, args, nargs, 1);
ret = njs_value_to_string(vm, value, value);
return ret;
}
- (void) njs_string_trim(vm, value, &string, NJS_TRIM_START);
-
- if (string.size == 0) {
- goto done;
- }
-
- p = string.start;
- end = p + string.size;
-
- minus = 0;
-
- if (p[0] == '-') {
- p++;
- minus = 1;
-
- } else if (p[0] == '+') {
- p++;
- }
-
- test_prefix = (end - p > 1);
- radix = 0;
-
if (nargs > 2) {
ret = njs_value_to_int32(vm, njs_argument(args, 2), &radix);
if (njs_slow_path(ret != NJS_OK)) {
if (radix < 2 || radix > 36) {
goto done;
}
-
- if (radix != 16) {
- test_prefix = 0;
- }
}
}
- if (radix == 0) {
- radix = 10;
- }
-
- if (test_prefix && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- p += 2;
- radix = 16;
- }
-
- num = njs_number_radix_parse(&p, end, radix);
+ (void) njs_string_trim(vm, value, &string, NJS_TRIM_START);
- num = minus ? -num : num;
+ num = njs_atod((char *) string.start, NULL, radix,
+ JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_PREFIX_AFTER_SIGN);
done:
double num;
njs_int_t ret;
njs_value_t *value, lvalue;
- njs_bool_t minus;
- const u_char *p, *start, *end;
njs_string_prop_t string;
value = njs_lvalue_arg(&lvalue, args, nargs, 1);
(void) njs_string_trim(vm, value, &string, NJS_TRIM_START);
- p = string.start;
- end = p + string.size;
-
- minus = 0;
-
- if (p == end) {
- num = NAN;
- goto done;
- }
-
- if (*p == '+') {
- p++;
-
- } else if (*p == '-') {
- p++;
- minus = 1;
- }
-
- start = p;
- num = njs_number_dec_parse(&p, end, 0);
-
- if (p == start) {
- if (p + njs_length("Infinity") > end
- || memcmp(p, "Infinity", njs_length("Infinity")) != 0)
- {
- num = NAN;
- goto done;
- }
-
- num = INFINITY;
- p += njs_length("Infinity");
- }
-
-done:
+ num = njs_atod((char *) string.start, NULL, 10, 0);
- njs_set_number(retval, minus ? -num : num);
+ njs_set_number(retval, num);
return NJS_OK;
}
#define _NJS_NUMBER_H_INCLUDED_
+typedef union {
+ double d;
+ uint64_t u64;
+} njs_diyfp_conv_t;
+
+
#define NJS_MAX_LENGTH (0x1fffffffffffffLL)
#define NJS_INT64_DBL_MIN (-9.223372036854776e+18) /* closest to INT64_MIN */
#define NJS_INT64_DBL_MAX (9.223372036854776e+18) /* closest to INT64_MAX */
+#define njs_uint64(h, l) (((uint64_t) (h) << 32) + (l))
+
+#define NJS_DBL_SIGNIFICAND_SIZE 52
+#define NJS_DBL_EXPONENT_OFFSET ((int64_t) 0x3ff)
+#define NJS_DBL_EXPONENT_BIAS (NJS_DBL_EXPONENT_OFFSET \
+ + NJS_DBL_SIGNIFICAND_SIZE)
+#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)
double njs_key_to_index(const njs_value_t *value);
-double njs_number_dec_parse(const u_char **start, const u_char *end,
- njs_bool_t literal);
-double njs_number_oct_parse(const u_char **start, const u_char *end,
- njs_bool_t literal);
-double njs_number_bin_parse(const u_char **start, const u_char *end,
- njs_bool_t literal);
double njs_number_hex_parse(const u_char **start, const u_char *end,
njs_bool_t literal);
njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
return NJS_ERROR;
}
- size = njs_sprintf(p, p + 10, "%uD", u32) - p;
+ size = njs_u32toa((char *) p, u32);
value->string.data->length = size;
value->string.data->size = size;
njs_string_prop_t string;
char buf[512];
-#define NJS_SZ_LAST 64
+#define NJS_SZ_LAST 64
+#define NJS_DTOA_MAX_LEN 25 /* njs_dtoa_max_len(d, .. JS_DTOA_FORMAT_FREE) */
if (njs_is_null_or_undefined(&args[0])) {
njs_type_error(vm, "\"this\" argument is null or undefined");
double
njs_string_to_number(njs_vm_t *vm, const njs_value_t *value)
{
+ int flags;
+ size_t len, size;
double num;
- njs_bool_t minus;
- const u_char *p, *start, *end;
+ const char *next;
+ const u_char *p;
njs_string_prop_t string;
- const size_t infinity = njs_length("Infinity");
-
(void) njs_string_trim(vm, value, &string, NJS_TRIM_START);
- p = string.start;
- end = p + string.size;
-
- if (p == end) {
+ if (string.size == 0) {
return 0.0;
}
- minus = 0;
+ p = string.start;
+ len = string.size;
+ flags = JS_ATOD_ACCEPT_BIN_OCT;
- if (p + 2 < end && p[0] == '0'
+ if (len >= 2 && p[0] == '0'
&& (p[1] == 'x' || p[1] == 'X'
|| p[1] == 'b' || p[1] == 'B'
|| p[1] == 'o' || p[1] == 'O'))
{
- p += 2;
-
- if (p[-1] == 'x' || p[-1] == 'X') {
- num = njs_number_hex_parse(&p, end, 0);
-
- } else if (p[-1] == 'b' || p[-1] == 'B') {
- num = njs_number_bin_parse(&p, end, 0);
-
- } else {
- num = njs_number_oct_parse(&p, end, 0);
- }
-
- } else {
-
- if (*p == '+') {
- p++;
-
- } else if (*p == '-') {
- p++;
- minus = 1;
- }
-
- start = p;
- num = njs_number_dec_parse(&p, end, 0);
+ flags |= JS_ATOD_INT_ONLY;
+ }
- if (p == start) {
- if (p + infinity > end || memcmp(p, "Infinity", infinity) != 0) {
- return NAN;
- }
+ num = njs_atod((char *) string.start, &next, 0, flags);
- num = INFINITY;
- p += infinity;
- }
- }
+ size = (u_char *) next - p;
- while (p < end) {
- if (!njs_is_whitespace(*p)) {
- return NAN;
+ /* Validate remaining characters are whitespace. */
+ while (size < string.size) {
+ if (!njs_is_whitespace(p[size])) {
+ num = NAN;
+ break;
}
- p++;
+ size++;
}
- return minus ? -num : num;
+ return num;
}
{
size_t size, len;
double num;
- njs_bool_t minus;
- const u_char *p, *start, *end;
- u_char buf[128];
+ const u_char *start;
+ char buf[128];
if (njs_slow_path(value->type == NJS_SYMBOL)) {
return NAN;
size = value->string.data->size;
start = value->string.data->start;
- p = start;
- end = p + size;
- minus = 0;
-
- if (size > 1) {
- switch (p[0]) {
- case '0':
- if (size != 1) {
- return NAN;
- }
-
- /* Fall through. */
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- break;
-
- case '-':
- if (size == 2 && p[1] == '0') {
- return -0.0;
- }
-
- if (size == njs_length("-Infinity")
- && memcmp(&p[1], "Infinity", njs_length("Infinity")) == 0)
- {
- return -INFINITY;
- }
-
- p++;
- minus = 1;
-
- break;
-
- case 'I':
- if (size == njs_length("Infinity")
- && memcmp(p, "Infinity", njs_length("Infinity")) == 0)
- {
- return INFINITY;
- }
-
- /* Fall through. */
-
- default:
- return NAN;
- }
- }
-
- num = njs_strtod(&p, end, 0);
- if (p != end) {
- return NAN;
+ if (size == 2 && start[0] == '-' && start[1] == '0') {
+ return -0.0;
}
- num = minus ? -num : num;
+ num = njs_atod((char *) start, NULL, 10, 0);
- len = njs_dtoa(num, (char *) buf);
+ len = njs_dtoa(num, buf);
if (size != len || memcmp(start, buf, size) != 0) {
return NAN;
}
+++ /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 <njs_main.h>
-#include <njs_diyfp.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 NJS_DECIMAL_POWER_MAX 309
-#define NJS_DECIMAL_POWER_MIN (-324)
-
-#define NJS_UINT64_MAX njs_uint64(0xFFFFFFFF, 0xFFFFFFFF)
-#define NJS_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.
- */
-njs_inline uint64_t
-njs_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 <= (NJS_UINT64_MAX / 10 - 1)) {
- d = *p++ - '0';
- value = 10 * value + d;
- }
-
- *ndigits = p - start;
-
- return value;
-}
-
-
-/*
- * Reads a njs_diyfp_t from the buffer.
- * The returned njs_diyfp_t is not necessarily normalized.
- * If remaining is zero then the returned njs_diyfp_t is accurate.
- * Otherwise it has been rounded and has error of at most 1/2 ulp.
- */
-static njs_diyfp_t
-njs_diyfp_read(const u_char *start, size_t length, int *remaining)
-{
- size_t read;
- uint64_t significand;
-
- significand = njs_read_uint64(start, length, &read);
-
- /* Round the significand. */
-
- if (length != read) {
- if (start[read] >= '5') {
- significand++;
- }
- }
-
- *remaining = length - read;
-
- return njs_diyfp(significand, 0);
-}
-
-
-/*
- * Returns 10^exp as an exact njs_diyfp_t.
- * The given exp must be in the range [1; NJS_DECIMAL_EXPONENT_DIST[.
- */
-njs_inline njs_diyfp_t
-njs_adjust_pow10(int exp)
-{
- switch (exp) {
- case 1:
- return njs_diyfp(njs_uint64(0xa0000000, 00000000), -60);
- case 2:
- return njs_diyfp(njs_uint64(0xc8000000, 00000000), -57);
- case 3:
- return njs_diyfp(njs_uint64(0xfa000000, 00000000), -54);
- case 4:
- return njs_diyfp(njs_uint64(0x9c400000, 00000000), -50);
- case 5:
- return njs_diyfp(njs_uint64(0xc3500000, 00000000), -47);
- case 6:
- return njs_diyfp(njs_uint64(0xf4240000, 00000000), -44);
- case 7:
- return njs_diyfp(njs_uint64(0x98968000, 00000000), -40);
- default:
- njs_unreachable();
- return njs_diyfp(0, 0);
- }
-}
-
-
-/*
- * 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
- * NJS_SIGNIFICAND_SIZE. The only exception are denormals. They start with
- * leading zeroes and their effective significand-size is hence smaller.
- */
-njs_inline int
-njs_diyfp_sgnd_size(int order)
-{
- if (order >= (NJS_DBL_EXPONENT_DENORMAL + NJS_SIGNIFICAND_SIZE)) {
- return NJS_SIGNIFICAND_SIZE;
- }
-
- if (order <= NJS_DBL_EXPONENT_DENORMAL) {
- return 0;
- }
-
- return order - NJS_DBL_EXPONENT_DENORMAL;
-}
-
-
-#define NJS_DENOM_LOG 3
-#define NJS_DENOM (1 << NJS_DENOM_LOG)
-
-/*
- * Returns either the correct double or the double that is just below
- * the correct double.
- */
-static double
-njs_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;
- njs_diyfp_t value, pow, adj_pow, rounded;
-
- value = njs_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 : NJS_DENOM / 2);
-
- orig_e = value.exp;
- value = njs_diyfp_normalize(value);
- error <<= orig_e - value.exp;
-
- if (exp < NJS_DECIMAL_EXPONENT_MIN) {
- return 0.0;
- }
-
- pow = njs_cached_power_dec(exp, &dec_exp);
-
- if (dec_exp != exp) {
-
- adj_exp = exp - dec_exp;
- adj_pow = njs_adjust_pow10(exp - dec_exp);
- value = njs_diyfp_mul(value, adj_pow);
-
- if (NJS_UINT64_DECIMAL_DIGITS_MAX - (int) length < adj_exp) {
- /*
- * The adjustment power is exact. There is hence only
- * an error of 0.5.
- */
- error += NJS_DENOM / 2;
- }
- }
-
- value = njs_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 / NJS_DENOM > error_a * error_b / 2^64.
- */
-
- error += NJS_DENOM + (error != 0 ? 1 : 0);
-
- orig_e = value.exp;
- value = njs_diyfp_normalize(value);
- error <<= orig_e - value.exp;
-
- /*
- * Check whether the double's significand changes when the error is added
- * or substracted.
- */
-
- magnitude = NJS_DIYFP_SIGNIFICAND_SIZE + value.exp;
- prec_digits = NJS_DIYFP_SIGNIFICAND_SIZE - njs_diyfp_sgnd_size(magnitude);
-
- if (prec_digits + NJS_DENOM_LOG >= NJS_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 + NJS_DENOM_LOG - NJS_DIYFP_SIGNIFICAND_SIZE + 1;
-
- value = njs_diyfp_shift_right(value, shift);
-
- /*
- * Add 1 for the lost precision of error, and NJS_DENOM
- * for the lost precision of value.significand.
- */
- error = (error >> shift) + 1 + NJS_DENOM;
- prec_digits -= shift;
- }
-
- prec_bits = value.significand & (((uint64_t) 1 << prec_digits) - 1);
- prec_bits *= NJS_DENOM;
-
- half_way = (uint64_t) 1 << (prec_digits - 1);
- half_way *= NJS_DENOM;
-
- rounded = njs_diyfp_shift_right(value, prec_digits);
-
- if (prec_bits >= half_way + error) {
- rounded.significand++;
- }
-
- return njs_diyfp2d(rounded);
-}
-
-
-static double
-njs_strtod_internal(const u_char *start, size_t length, int exp)
-{
- int shift;
- 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;
- }
-
- shift = (int) (left - right);
-
- if (exp >= NJS_DECIMAL_POWER_MAX - shift - (int) length + 1) {
- return INFINITY;
- }
-
- if (exp <= NJS_DECIMAL_POWER_MIN - shift - (int) length) {
- return 0.0;
- }
-
- return njs_diyfp_strtod(start, length, exp + shift);
-}
-
-
-double
-njs_strtod(const u_char **start, const u_char *end, njs_bool_t literal)
-{
- int exponent, exp, insignf;
- u_char c, *pos;
- njs_bool_t minus;
- const u_char *e, *p, *last, *_;
- u_char data[128];
-
- exponent = 0;
- insignf = 0;
-
- pos = data;
- last = data + sizeof(data);
-
- p = *start;
- _ = p - 2;
-
- for (; p < end; p++) {
- /* Values less than '0' become >= 208. */
- c = *p - '0';
-
- if (njs_slow_path(c > 9)) {
- if (literal) {
- if ((p - _) == 1) {
- goto done;
- }
-
- if (*p == '_') {
- _ = p;
- continue;
- }
- }
-
- break;
- }
-
- if (pos < last) {
- *pos++ = *p;
-
- } else {
- insignf++;
- }
- }
-
- /* Do not emit a '.', but adjust the exponent instead. */
- if (p < end && *p == '.') {
- _ = p;
-
- for (p++; p < end; p++) {
- /* Values less than '0' become >= 208. */
- c = *p - '0';
-
- if (njs_slow_path(c > 9)) {
- if (literal && *p == '_' && (p - _) > 1) {
- _ = p;
- continue;
- }
-
- break;
- }
-
- if (pos < last) {
- *pos++ = *p;
- exponent--;
-
- } else {
- /* Ignore insignificant digits in the fractional part. */
- }
- }
- }
-
- if (pos == data) {
- return NAN;
- }
-
- 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 (njs_fast_path(c <= 9)) {
- exp = c;
-
- for (p = e + 1; p < end; p++) {
- /* Values less than '0' become >= 208. */
- c = *p - '0';
-
- if (njs_slow_path(c > 9)) {
- if (literal && *p == '_' && (p - _) > 1) {
- _ = p;
- continue;
- }
-
- break;
- }
-
- if (exp < (INT_MAX - 9) / 10) {
- exp = exp * 10 + c;
- }
- }
-
- exponent += minus ? -exp : exp;
-
- } else if (literal && *e == '_') {
- p = e;
- }
- }
-
-done:
-
- *start = p;
-
- exponent += insignf;
-
- return njs_strtod_internal(data, pos - data, exponent);
-}
+++ /dev/null
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) Nginx, Inc.
- */
-
-#ifndef _NJS_STRTOD_H_INCLUDED_
-#define _NJS_STRTOD_H_INCLUDED_
-
-NJS_EXPORT double njs_strtod(const u_char **start, const u_char *end,
- njs_bool_t literal);
-
-#endif /* _NJS_STRTOD_H_INCLUDED_ */
#endif
-#if (NJS_HAVE_UNSIGNED_INT128)
-typedef unsigned __int128 njs_uint128_t;
-#endif
-
-
#if (NJS_INT_T_SIZE == 8)
#define NJS_INT_T_LEN NJS_INT64_T_LEN
#define NJS_INT_T_HEXLEN NJS_INT64_T_HEXLEN
njs_str("SyntaxError: Unexpected token \".\" in 1") },
{ njs_str("0_1"),
- njs_str("SyntaxError: Unexpected token \"0_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0_1\" in 1") },
{ njs_str("1_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1_\" in 1") },
{ njs_str("1__0"),
- njs_str("SyntaxError: Unexpected token \"__0\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1__0\" in 1") },
{ njs_str("._1"),
njs_str("SyntaxError: Unexpected token \".\" in 1") },
{ njs_str(".1_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \".1_\" in 1") },
{ njs_str("1_.1"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1_\" in 1") },
{ njs_str(".0__1"),
- njs_str("SyntaxError: Unexpected token \"__1\" in 1") },
+ njs_str("SyntaxError: Unexpected token \".0__1\" in 1") },
{ njs_str("1e_1"),
- njs_str("SyntaxError: Unexpected token \"_1\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1e_1\" in 1") },
{ njs_str("1e-_1"),
- njs_str("SyntaxError: Unexpected token \"_1\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1e\" in 1") },
{ njs_str("1E1__0"),
- njs_str("SyntaxError: Unexpected token \"__0\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1E1__0\" in 1") },
{ njs_str("1_e1"),
- njs_str("SyntaxError: Unexpected token \"_e1\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1_e1\" in 1") },
{ njs_str("1e1_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1e1_\" in 1") },
{ njs_str("-_1"),
njs_str("ReferenceError: \"_1\" is not defined") },
njs_str("SyntaxError: Unexpected token \"0O778\" in 1") },
{ njs_str("0O_7"),
- njs_str("SyntaxError: Unexpected token \"0O\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0O_7\" in 1") },
{ njs_str("0O + 1"),
njs_str("SyntaxError: Unexpected token \"0O\" in 1") },
{ njs_str("0o7_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0o7_\" in 1") },
{ njs_str("0o7__7"),
- njs_str("SyntaxError: Unexpected token \"__7\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0o7__7\" in 1") },
+
+ { njs_str("0o7.1"),
+ njs_str("SyntaxError: Unexpected token \".1\" in 1") },
/* Legacy Octal Numbers are deprecated. */
njs_str("SyntaxError: Unexpected token \"09\" in 1") },
{ njs_str("0011"),
- njs_str("SyntaxError: Unexpected token \"00\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0011\" in 1") },
{ njs_str("0_"),
njs_str("SyntaxError: Unexpected token \"0_\" in 1") },
{ njs_str("0_1"),
- njs_str("SyntaxError: Unexpected token \"0_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0_1\" in 1") },
{ njs_str("00_1"),
- njs_str("SyntaxError: Unexpected token \"00\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"00_1\" in 1") },
/* Binary Numbers. */
njs_str("SyntaxError: Unexpected token \"0B12\" in 1") },
{ njs_str("0b_11"),
- njs_str("SyntaxError: Unexpected token \"0b\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0b_11\" in 1") },
{ njs_str("0b + 1"),
njs_str("SyntaxError: Unexpected token \"0b\" in 1") },
{ njs_str("0B1__1"),
- njs_str("SyntaxError: Unexpected token \"__1\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0B1__1\" in 1") },
{ njs_str("0b11_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0b11_\" in 1") },
+
+ { njs_str("0b1.1"),
+ njs_str("SyntaxError: Unexpected token \".1\" in 1") },
/* Hex Numbers. */
njs_str("SyntaxError: Unexpected end of input in 1") },
{ njs_str("0x12g"),
- njs_str("SyntaxError: Unexpected token \"g\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0x12g\" in 1") },
{ njs_str("0X_ff"),
- njs_str("SyntaxError: Unexpected token \"0X\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0X_ff\" in 1") },
{ njs_str("0X + 1"),
njs_str("SyntaxError: Unexpected token \"0X\" in 1") },
{ njs_str("0xff_"),
- njs_str("SyntaxError: Unexpected token \"_\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0xff_\" in 1") },
{ njs_str("0Xf__f"),
- njs_str("SyntaxError: Unexpected token \"__f\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"0Xf__f\" in 1") },
{ njs_str(""),
njs_str("undefined") },
njs_str("-Infinity") },
{ njs_str("1e"),
- njs_str("SyntaxError: Unexpected token \"e\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1e\" in 1") },
{ njs_str("1.e"),
- njs_str("SyntaxError: Unexpected token \"e\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1.e\" in 1") },
{ njs_str("1e+"),
- njs_str("SyntaxError: Unexpected token \"e\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1e\" in 1") },
{ njs_str("1.e-"),
- njs_str("SyntaxError: Unexpected token \"e\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1.e\" in 1") },
{ njs_str("1eZ"),
- njs_str("SyntaxError: Unexpected token \"eZ\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"1eZ\" in 1") },
{ njs_str(".e1"),
njs_str("SyntaxError: Unexpected token \".\" in 1") },
#ifndef NJS_SUNC
{ njs_str("1e20.toString(14)"),
- njs_str("33cb3bb449c2a92000") },
+ njs_str("33cb3bb449c2a90000") },
- { njs_str("1.7976931348623157E+308.toString(36) == ('1a1e4vngaiqo' + '0'.repeat(187))"),
+ { njs_str("1.7976931348623157E+308.toString(36) == ('1a1e4vngail' + '0'.repeat(188))"),
njs_str("true") },
/* Largest positive double (prev_double(INFINITY)). */
njs_str("1.2312313132,1.25a850416057383,1.293699002749414,1.3010274cab0288,1.3346da6d5d455c") },
{ njs_str("Array(5).fill().map((n, i) => 36 - i).map((v)=>(1e23).toString(v))"),
- njs_str("ga894a06abs0000,o5hlsorok4y0000,128fpsprqld20000,1m1s0ajv6cmo0000,2kmg5hv19br00000") },
+ njs_str("ga894a06ac00000,o5hlsorok5a0000,128fpsprqldf0000,1m1s0ajv6cn00000,2kmg5hv19br00000") },
#endif
/* Number.prototype.toFixed(frac) method. */
{ njs_str("NaN.toFixed(1)"),
njs_str("NaN") },
-#if 0 /* FIXME: bignum support is requred to support frac >= 20 */
{ njs_str("(2 ** -100).toFixed(100)"),
njs_str("0.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640625") },
-#endif
/* Number.prototype.toPrecision(prec) method. */
njs_str("0.125000,0.250000,0.500000,1.00000,2.00000,4.00000,8.00000,16.0000,32.0000,64.0000,128.000,256.000") },
{ njs_str("Array(5).fill().map((n, i) => i+16).map((v)=>(4.1).toPrecision(v))"),
- njs_str("4.100000000000000,4.0999999999999996,4.09999999999999964,4.099999999999999644,4.0999999999999996447") },
+ njs_str("4.100000000000000,4.0999999999999996,4.09999999999999964,4.099999999999999645,4.0999999999999996447") },
{ njs_str("Array(3).fill().map((n, i) => i + 19).map((v)=>(2**(-v)).toPrecision(20))"),
njs_str("0.0000019073486328125000000,9.5367431640625000000e-7,4.7683715820312500000e-7") },
{ njs_str("Array(3).fill().map((n, i) => i + 32).map((v)=>(2**(v)+0.1).toPrecision(10))"),
njs_str("4294967296,8589934592,1.717986918e+10") },
-#if 0 /* FIXME: bignum support is requred to support prec >= 20 */
{ njs_str("(1/7).toPrecision(100)"),
njs_str("0.1428571428571428492126926812488818541169166564941406250000000000000000000000000000000000000000000000") },
{ njs_str("(2**128).toPrecision(40)"),
njs_str("340282366920938463463374607431768211456.0") },
-#endif
{ njs_str("(2**128).toPrecision(1)"),
njs_str("3e+38") },
njs_str("3.4e+38") },
{ njs_str("(2**128).toPrecision(40)"),
- njs_str("340282366920938463490000000000000000000.0") },
+ njs_str("340282366920938463463374607431768211456.0") },
{ njs_str("(123).toPrecision(0)"),
njs_str("RangeError: precision argument must be between 1 and 100") },
njs_str("-6.66667e-1,1.33333e+0,-2.00000e+0,2.66667e+0,-3.33333e+0,4.00000e+0") },
{ njs_str("Array(5).fill().map((n, i) => i + 1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toExponential())"),
- njs_str("-6.666666666666666e-1,1.3333333333333333e+0,-2e+0,2.6666666666666667e+0,-3.3333333333333337e+0") },
+ njs_str("-6.666666666666666e-1,1.3333333333333333e+0,-2e+0,2.6666666666666665e+0,-3.3333333333333335e+0") },
{ njs_str("1.7976931348623157e+308.toExponential()"),
njs_str("1.7976931348623157e+308") },
-#if 0 /* FIXME: bignum support is requred to support prec >= 20 */
{ njs_str("(1/7).toExponential(100)"),
njs_str("1.4285714285714284921269268124888185411691665649414062500000000000000000000000000000000000000000000000e-1") },
-#endif
{ njs_str("var v = 1.7976931348623157e+308; Number(v.toExponential()) == v"),
njs_str("true") },
{ njs_str("9007199254740993 | 0"),
njs_str("0") },
-#if 0
{ njs_str("9223372036854775808 | 0"),
njs_str("0") },
-#endif
{ njs_str("9223372036854777856 | 0"),
njs_str("2048") },
{ njs_str("-2147483648 << 0"),
njs_str("-2147483648") },
-#if 0
{ njs_str("9223372036854775808 << 0"),
njs_str("0") },
-#endif
{ njs_str("9223372036854777856 << 0"),
njs_str("2048") },
{ njs_str("-2147483648 >> -1"),
njs_str("-1") },
-#if 0
{ njs_str("9223372036854775808 >> 0"),
njs_str("0") },
-#endif
{ njs_str("9223372036854777856 >> 0"),
njs_str("2048") },
{ njs_str("NaN >>> 1"),
njs_str("0") },
-#if 0
{ njs_str("9223372036854775808 >>> 1"),
njs_str("0") },
-#endif
{ njs_str("-1 >>> 0"),
njs_str("4294967295") },
{ njs_str("-2147483648 >>> -1"),
njs_str("1") },
-#if 0
{ njs_str("9223372036854775808 >>> 0"),
njs_str("0") },
-#endif
{ njs_str("9223372036854777856 >>> 0"),
njs_str("2048") },
{ njs_str("-2147483648 & 65536"),
njs_str("0") },
-#if 0
{ njs_str("9223372036854775808 & 65536"),
njs_str("0") },
-#endif
{ njs_str("NaN & 65536"),
njs_str("0") },
{ njs_str("-2147483648 ^ 65536"),
njs_str("-2147418112") },
-#if 0
{ njs_str("9223372036854775808 ^ 65536"),
njs_str("65536") },
-#endif
{ njs_str("NaN ^ 65536"),
njs_str("65536") },
njs_str("SyntaxError: Unexpected token \")\" in 1") },
{ njs_str("for(9A=>>"),
- njs_str("SyntaxError: Unexpected token \"A\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"9A\" in 1") },
{ njs_str("for(A?{,"),
njs_str("SyntaxError: Unexpected token \",\" in 1") },
"2172814084,75727489,2189624325,84181890,"
"-2122153212,75727489,-2105342971,84181890,"
"-4.794245620412925e-38,3.091780090135418e-36,-1.9251027092506622e-37,6.230764342760857e-36,"
- "-2.159546358334202e-301,5.447603729090798e-270,-1.4538065947240604e-296,3.72581468952343e-265") },
+ "-2.159546358334202e-301,5.447603729090798e-270,-1.4538065947240603e-296,3.72581468952343e-265") },
{ njs_str("var u8 = new Uint8Array(10);"
"var dv = new DataView(u8.buffer, 1);"
njs_str("1") },
{ njs_str("(function f() { return 2.toString(); })()"),
- njs_str("SyntaxError: Unexpected token \"toString\" in 1") },
+ njs_str("SyntaxError: Unexpected token \"2.toString\" in 1") },
{ njs_str("(function f() { return 2..toString(); })()"),
njs_str("2") },
{ njs_str("Number('0B111')"),
njs_str("7") },
+ { njs_str("Number('0B111 ')"),
+ njs_str("7") },
+
+ { njs_str("Number('0B111 1')"),
+ njs_str("NaN") },
+
{ njs_str("Number('0b1_11')"),
njs_str("NaN") },
{ njs_str("Number('-0b111')"),
njs_str("NaN") },
+ { njs_str("Number('0b111.')"),
+ njs_str("NaN") },
+
+ { njs_str("Number('0b111.1')"),
+ njs_str("NaN") },
+
{ njs_str("Number(123)"),
njs_str("123") },
{ njs_str("Number('0O123')"),
njs_str("83") },
+ { njs_str("Number('0O123 ')"),
+ njs_str("83") },
+
{ njs_str("Number('0o1_23')"),
njs_str("NaN") },
{ njs_str("Number('-0o123')"),
njs_str("NaN") },
+ { njs_str("Number('0o123.')"),
+ njs_str("NaN") },
+
+ { njs_str("Number('0o123.4')"),
+ njs_str("NaN") },
+
{ njs_str("Number('0x123')"),
njs_str("291") },
{ njs_str("Number('0X123')"),
njs_str("291") },
+ { njs_str("Number('0X123 ')"),
+ njs_str("291") },
+
{ njs_str("Number('0x1_23')"),
njs_str("NaN") },
{ njs_str("Number('-0x123')"),
njs_str("NaN") },
+ { njs_str("Number('0x123.')"),
+ njs_str("NaN") },
+
+ { njs_str("Number('0x123.4')"),
+ njs_str("NaN") },
+
{ njs_str("['1', ' 1 ', '1\\t', '1\\n', '1\\r\\n'].reduce((a, x) => a + Number(x), 0)"),
njs_str("5") },
njs_str("0.1111111111111111") },
{ njs_str("Number('1'.repeat(128))"),
- njs_str("1.1111111111111113e+127") },
+ njs_str("1.1111111111111112e+127") },
{ njs_str("Number('1'.repeat(129))"),
njs_str("1.1111111111111112e+128") },
{ njs_str("parseFloat('-5.7e+abc')"),
njs_str("-5.7") },
+ { njs_str("parseFloat('5' + '0'.repeat(200))"),
+ njs_str("5e+200") },
+
+ { njs_str("parseFloat('500_000_000')"),
+ njs_str("500") },
+
/* debugger. */
{ njs_str("debugger"),
njs_str("SyntaxError: Unexpected number at position 0") },
{ njs_str("JSON.parse('--')"),
- njs_str("SyntaxError: Unexpected number at position 1") },
+ njs_str("SyntaxError: Unexpected number at position 0") },
{ njs_str("JSON.parse('1-')"),
njs_str("SyntaxError: Unexpected token at position 1") },
{ njs_str("2.2250738585072014E-308.toString(2) == ('0.' + '0'.repeat(1021) + '1')"),
njs_str("true") },
+ { njs_str("Number.MAX_VALUE.toString(2) === ('1'.repeat(53) + '0'.repeat(971))"),
+ njs_str("true") },
+
{ njs_str("Number('2.2250738585072014E-323')"),
njs_str("2.5e-323") },
{ njs_str("var zeros = count => '0'.repeat(count);"
"["
" [1.8858070859709815e-308, `0.${zeros(1022)}1101100011110111011100000100011001111101110001010111`],"
- // FIXME: " [Number.MIN_VALUE, `0.${zeros(1073)}1`]"
+ " [Number.MIN_VALUE, `0.${zeros(1021)}1`],"
" [-5.06631661953108e-309, `-0.${zeros(1024)}11101001001010000001101111010101011111111011010111`],"
" [6.22574126804e-313, `0.${zeros(1037)}11101010101101100111000110100111001`],"
" [-4e-323, `-0.${zeros(1070)}1`],"
};
-static njs_unit_test_t njs_disabled_denormals_test[] =
-{
- { njs_str("Number('2.2250738585072014E-323')"),
- njs_str("0") },
-
- { njs_str("Number('2.2250738585072014E-323') + 0"),
- njs_str("0") },
-
- /* Smallest positive double (next_double(0)). */
- { njs_str("5E-324.toString(36)"),
- njs_str("0") },
-
- { njs_str("2.2250738585072014E-323.toString(2)"),
- njs_str("0") },
-
- /* Smallest normal double. */
-
- { njs_str("2.2250738585072014e-308"),
- njs_str("2.2250738585072014e-308") },
-
- { njs_str("2.2250738585072014e-308/2"),
- njs_str("0") },
-
- /* Denormals. */
- { njs_str("["
- "1.8858070859709815e-308,"
- "-5.06631661953108e-309,"
- "6.22574126804e-313,"
- "-4e-323,"
- "].map(v=>v.toString(2))"),
- njs_str("0,0,0,0") },
-};
-
-
static njs_unit_test_t njs_fs_module_test[] =
{
{ njs_str("var fs = require('fs'); typeof fs"),
} njs_test_suite_t;
-static njs_int_t
-njs_disabled_denormals_tests(njs_unit_test_t tests[], size_t num,
- njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat)
-{
- njs_int_t ret;
-
- njs_mm_denormals(0);
-
- ret = njs_unit_test(tests, num, name, opts, stat);
-
- njs_mm_denormals(1);
-
- return ret;
-}
-
-
static njs_test_suite_t njs_suites[] =
{
{ njs_str("script"),
njs_nitems(njs_denormals_test),
njs_unit_test },
- {
-#if (NJS_HAVE_DENORMALS_CONTROL)
- njs_str("disabled denormals"),
-#else
- njs_str(""),
-#endif
- { .repeat = 1, .unsafe = 1 },
- njs_disabled_denormals_test,
- njs_nitems(njs_disabled_denormals_test),
- njs_disabled_denormals_tests },
-
{ njs_str("module"),
{ .repeat = 1, .module = 1, .unsafe = 1 },
njs_module_test,
tzset();
- njs_mm_denormals(1);
-
njs_memzero(&stat, sizeof(njs_stat_t));
for (i = 0; i < njs_nitems(njs_suites); i++) {