[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