// Numbers: conversions between numbers and strings #pragma once #include "str.h" // JS's Number::toString string_t __rt_number_to_string(double x); // JS's ToNumber on a string double __rt_string_to_number(string_t s); #include // JS's `x ** y`: C's pow, except that a NaN exponent always gives NaN, and so does // an infinite exponent when |x| is 1. C gives 1 for both static inline double __rt_pow(double x, double y) { if (isnan(y) || (isinf(y) && fabs(x) == 1)) return NAN; return pow(x, y); } // JS's ToInt32: the number modulo 2^32, as a signed int. NaN and the infinities are 0 static inline int32_t __rt_to_int32(double x) { if (!isfinite(x)) return 0; double m = fmod(trunc(x), 4294967296.0); if (m < 0) m += 4294967296.0; return (int32_t)(uint32_t)m; } #ifdef JSRT_IMPLEMENTATION #include #include #include #include // JS's Number::toString: the fewest digits that give the number back. Plain // notation while the decimal point is within 21 places, else exponential string_t __rt_number_to_string(double x) { if (isnan(x)) return __rt_string_lit("NaN"); if (isinf(x)) return __rt_string_lit(x < 0 ? "-Infinity" : "Infinity"); if (x == 0) return __rt_string_lit("0"); // The fewest digits: buf is [-]d[.ddd]e±xx char buf[64]; for (int precision = 0; precision < 17; precision++) { snprintf(buf, sizeof buf, "%.*e", precision, x); if (strtod(buf, NULL) == x) { break; } } char digits[32], out[64]; char *p = buf, *o = out; int n = 0; if (*p == '-') { *o++ = *p++; } for (; *p != 'e'; p++) { if (*p != '.') { digits[n++] = *p; } } digits[n] = 0; // The decimal point goes after the first `point` digits int point = atoi(p + 1) + 1; if (n <= point && point <= 21) { o += sprintf(o, "%s", digits); for (int i = n; i < point; i++) *o++ = '0'; *o = 0; } else if (0 < point && point <= 21) { sprintf(o, "%.*s.%s", point, digits, digits + point); } else if (-6 < point && point <= 0) { o += sprintf(o, "0."); for (int i = point; i < 0; i++) *o++ = '0'; sprintf(o, "%s", digits); } else { int e = point - 1; sprintf(o, "%c%s%se%c%d", digits[0], n > 1 ? "." : "", digits + 1, e < 0 ? '-' : '+', abs(e)); } return __rt_string_lit(out); } string_t __rt_number_to_fixed(double x, int32_t digits) { char buf[400]; snprintf(buf, sizeof buf, "%.*f", digits, x); return __rt_string_lit(buf); } // JS's ToNumber on a string: a decimal number with whitespace around it, or // only whitespace, which is 0. Anything else is NaN double __rt_string_to_number(string_t s) { char *utf8 = __rt_string_utf8(s); char *start = utf8, *end; while (*start == ' ' || (*start >= '\t' && *start <= '\r')) start++; double x = 0; if (*start) { x = strtod(start, &end); while (*end == ' ' || (*end >= '\t' && *end <= '\r')) end++; if (*end || start == end) x = NAN; } free(utf8); return x; } #endif // JSRT_IMPLEMENTATION