[njs] Added Number.prototype.toExponential().

Dmitry Volyntsev xeioex at nginx.com
Mon Oct 7 15:37:47 UTC 2019


details:   https://hg.nginx.org/njs/rev/c9061ff75070
branches:  
changeset: 1174:c9061ff75070
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Oct 07 18:16:47 2019 +0300
description:
Added Number.prototype.toExponential().

diffstat:

 src/njs_dtoa.c           |  80 +++++++++++++++++++++++++++++++++++++----------
 src/njs_dtoa.h           |   2 +
 src/njs_number.c         |  55 +++++++++++++++++++++++++++++++++
 src/test/njs_unit_test.c |  48 ++++++++++++++++++++++++++++
 4 files changed, 168 insertions(+), 17 deletions(-)

diffs (242 lines):

diff -r d6ba4bffddef -r c9061ff75070 src/njs_dtoa.c
--- a/src/njs_dtoa.c	Mon Oct 07 18:11:19 2019 +0300
+++ b/src/njs_dtoa.c	Mon Oct 07 18:16:47 2019 +0300
@@ -472,30 +472,36 @@ njs_dtoa_format(char *start, size_t len,
 
 
 njs_inline size_t
+njs_dtoa_exp_format(char *start, int exponent, size_t prec, size_t len)
+{
+    char  *p;
+
+    p = &start[len];
+    if (prec != 1) {
+        memmove(&start[2], &start[1], len - 1);
+        start[1] = '.';
+        p++;
+    }
+
+    njs_memset(p, '0', prec - len);
+    p += prec - len;
+
+    *p++ = 'e';
+
+    return prec + 1 + (prec != 1) + njs_write_exponent(exponent, p);
+}
+
+
+njs_inline size_t
 njs_dtoa_prec_format(char *start, size_t prec, size_t len, int point)
 {
     int     exponent;
-    char    *p;
-    size_t  m, rest, size;
+    size_t  m, rest;
 
     exponent = point - 1;
 
     if (exponent < -6 || exponent >= (int) prec) {
-        p = &start[len];
-        if (prec != 1) {
-            memmove(&start[2], &start[1], len - 1);
-            start[1] = '.';
-            p++;
-        }
-
-        njs_memset(p, '0', prec - len);
-        p += prec - len;
-
-        *p++ = 'e';
-
-        size = njs_write_exponent(exponent, p);
-
-        return prec + 1 + (prec != 1) + size;
+        return njs_dtoa_exp_format(start, exponent, prec, len);
     }
 
     /* Fixed notation. */
@@ -609,3 +615,43 @@ njs_dtoa_precision(double value, char *s
 
     return njs_dtoa_prec_format(p, prec, length, point) + minus;
 }
+
+
+size_t
+njs_dtoa_exponential(double value, char *start, njs_int_t frac)
+{
+    int     point, minus;
+    char    *p;
+    size_t  length;
+
+    /* Not handling NaN and inf. */
+
+    p = start;
+    minus = 0;
+
+    if (value != 0) {
+        if (value < 0) {
+            *p++ = '-';
+            value = -value;
+            minus = 1;
+        }
+
+        if (frac == -1) {
+            length = njs_grisu2(value, p, &point);
+
+        } else {
+            length = njs_grisu2_prec(value, p, frac + 1, &point);
+        }
+
+    } else {
+        start[0] = '0';
+        length = 1;
+        point = 1;
+    }
+
+    if (frac == -1) {
+        frac = length - 1;
+    }
+
+    return njs_dtoa_exp_format(p, point - 1, frac + 1, length) + minus;
+}
diff -r d6ba4bffddef -r c9061ff75070 src/njs_dtoa.h
--- a/src/njs_dtoa.h	Mon Oct 07 18:11:19 2019 +0300
+++ b/src/njs_dtoa.h	Mon Oct 07 18:16:47 2019 +0300
@@ -9,5 +9,7 @@
 
 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);
 
 #endif /* _NJS_DTOA_H_INCLUDED_ */
diff -r d6ba4bffddef -r c9061ff75070 src/njs_number.c
--- a/src/njs_number.c	Mon Oct 07 18:11:19 2019 +0300
+++ b/src/njs_number.c	Mon Oct 07 18:16:47 2019 +0300
@@ -684,6 +684,52 @@ njs_number_prototype_to_precision(njs_vm
 }
 
 
+static njs_int_t
+njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    double       number;
+    size_t       size;
+    int32_t      frac;
+    njs_value_t  *value;
+    u_char       buf[128];
+
+    value = &args[0];
+
+    if (value->type != NJS_NUMBER) {
+        if (value->type == NJS_OBJECT_NUMBER) {
+            value = njs_object_value(value);
+
+        } else {
+            njs_type_error(vm, "unexpected value type:%s",
+                           njs_type_string(value->type));
+            return NJS_ERROR;
+        }
+    }
+
+    number = njs_number(value);
+
+    if (njs_slow_path(isnan(number) || isinf(number))) {
+        return njs_number_to_string(vm, &vm->retval, value);
+    }
+
+    if (njs_is_defined(njs_arg(args, nargs, 1))) {
+        frac = njs_primitive_value_to_integer(njs_argument(args, 1));
+        if (njs_slow_path(frac < 0 || frac > 100)) {
+            njs_range_error(vm, "digits argument must be between 0 and 100");
+            return NJS_ERROR;
+        }
+
+    } else {
+        frac = -1;
+    }
+
+    size = njs_dtoa_exponential(number, (char *) buf, frac);
+
+    return njs_string_new(vm, &vm->retval, buf, size, size);
+}
+
+
 /*
  * The radix equal to 2 produces the longest  value for a number.
  */
@@ -858,6 +904,15 @@ static const njs_object_prop_t  njs_numb
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("toExponential"),
+        .value = njs_native_function(njs_number_prototype_to_exponential,
+                                     NJS_SKIP_ARG, NJS_INTEGER_ARG),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
diff -r d6ba4bffddef -r c9061ff75070 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Oct 07 18:11:19 2019 +0300
+++ b/src/test/njs_unit_test.c	Mon Oct 07 18:16:47 2019 +0300
@@ -628,6 +628,54 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("(0).toFixed(101)"),
       njs_str("RangeError: digits argument must be between 0 and 100") },
 
+    /* Number.prototype.toExponential(frac) method. */
+
+    { njs_str("Array(3).fill().map((n, i) => i + 1).map((v)=>(0).toExponential(v))"),
+      njs_str("0.0e+0,0.00e+0,0.000e+0") },
+
+    { njs_str("Array(6).fill().map((n, i) => i + 1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toExponential(5))"),
+      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") },
+
+#ifndef NJS_SUNC
+    { njs_str("4.94065645841246544176568792868e-324.toExponential()"),
+      njs_str("5e-324") },
+
+    { njs_str("4.94065645841246544176568792868e-324.toExponential(10)"),
+      njs_str("4.9406564584e-324") },
+#endif
+
+    { 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("(123).toExponential(-1)"),
+      njs_str("RangeError: digits argument must be between 0 and 100") },
+
+    { njs_str("(123).toExponential(2.4)"),
+      njs_str("1.23e+2") },
+
+    { njs_str("(123).toExponential(101)"),
+      njs_str("RangeError: digits argument must be between 0 and 100") },
+
+    { njs_str("[2**10000,-(2**10000),NaN].map((v)=>v.toExponential())"),
+      njs_str("Infinity,-Infinity,NaN") },
+
+    { njs_str("[2**10000,-(2**10000),NaN].map((v)=>v.toExponential(1000))"),
+      njs_str("Infinity,-Infinity,NaN") },
+
+    { njs_str("Number.prototype.toExponential.call('12')"),
+      njs_str("TypeError: unexpected value type:string") },
+
     /* An object "valueOf/toString" methods. */
 
     { njs_str("var a = { valueOf: function() { return 1 } };    +a"),


More information about the nginx-devel mailing list