33#include <NDEVR/String.h>
34#include <NDEVR/BaseValues.h>
35#include <NDEVR/NumberParser.h>
40 static const fltp08 kPowersOfTen[23] =
42 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
43 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
56 return kPowersOfTen[exp];
65 static constexpr fltp08 kLog2_10 = 0.30102999566398119521373889472449;
66 static constexpr uint04 maxBase2Precision = 53;
67 static constexpr uint08 two_pow_maxBase2PrecisionMinus1 =
uint08(1) << (maxBase2Precision - 1);
70 if (0 < exp && exp < 64)
77 D2A(
const fltp08 avalue,
bool fixedPrecision,
uint04 minPrecision)
82 mantissa = n_frexp(value, &e);
86 lowOk = highOk =
true;
90 bool round = (mantissa & 1) == 0;
91 lowOk = highOk = round;
94 mantissaPrec = maxBase2Precision;
95 while (mantissaPrec != 0 && (((mantissa >> --mantissaPrec) & 1) == 0)) {}
99 if (mantissa != two_pow_maxBase2PrecisionMinus1)
101 const fltp08 be = quickPowTwo(e);
110 fltp08 be = quickPowTwo(e);
119 else if (mantissa != two_pow_maxBase2PrecisionMinus1)
123 ds = quickPowTwo(1 - e);
130 ds = quickPowTwo(2 - e);
136 const fltp08 fixedPrecisionPowTen = quickPowTen(minPrecision);
137 ds *= fixedPrecisionPowTen;
138 dr *= fixedPrecisionPowTen;
142 uint08 n_frexp(
double v,
int* eptr)
const
144 fltp08 fracMantissa = std::frexp(v, eptr);
146 return (uint64_t)(fracMantissa * (double)(1LL << 53));
153 bool withinLowEndRoundRange;
154 bool withinHighEndRoundRange;
158 lib_assert(quotient < 10, "quotient cannot be > 9
");
159 double mod = fmod(dr, ds);
162 // check if remaining ratio r/s is within the range of floats which would round to the value we have output
163 // so far when read in from a string.
164 withinLowEndRoundRange = (lowOk ? (dr <= dMMinus) : (dr < dMMinus));
165 withinHighEndRoundRange = (highOk ? (dr + dMPlus >= ds) : (dr + dMPlus > ds));
168 if (!withinLowEndRoundRange)
170 if (!withinHighEndRoundRange) // if not within either error range, set up to generate the next digit.
182 else if (!withinHighEndRoundRange)
188 if (dr * 2 < ds) // if (r*2 < s) todo: faster to do lshift and compare?
200 sint04 expBase10() { return base10Exp; }
202 fltp08 value; // Double value for quick work when e and mantissa are small.
204 uint08 mantissa; // On input, value = mantissa*2^e; Only last 53 bits are used.
205 sint04 mantissaPrec; // The number of bits of precision that are present in the mantissa.
206 sint04 base10Exp; // The (derived) base 10 exponent of value.
207 bool finished; // Set to true when we've output all relevant digits.
210 bool lowOk; // For IEEE unbiased rounding, this is true when mantissa is even. When true, use >= in mMinus test instead of >
211 bool highOk; // Ditto, but for mPlus test.
213 // If !bFastEstimateOk, use these.
215 // If bFastEstimateOk, use these - same as above, but integer value stored in double.
221 // Estimate base 10 exponent of number, scale r,s,mPlus,mMinus appropriately.
222 // Returns result of fixup_ExponentEstimate(est).
225 // estimate base10 exponent:
226 sint04 base2Exponent = e + mantissaPrec - 1;
227 sint04 exponentEstimate = (sint04)ceil((base2Exponent * kLog2_10) - 0.0000000001);
229 fltp08 scale = quickPowTen((exponentEstimate > 0) ? exponentEstimate : -exponentEstimate);
231 if (exponentEstimate >= 0)
234 return fixup_ExponentEstimate(exponentEstimate);
241 return fixup_ExponentEstimate(exponentEstimate);
245 // Used by scale to adjust for possible off-by-one error in the base 10 exponent estimate.
246 // Returns exact base10 exponent of number.
247 sint04 fixup_ExponentEstimate(sint04 exponentEstimate)
249 sint04 correctedEstimate;
250 uint01 quotient = cast<uint01>((dr * 10) / ds);
251 if ((highOk ? (dr + dMPlus) >= ds : dr + dMPlus > ds) || quotient >= 10)
253 correctedEstimate = exponentEstimate + 1;
260 correctedEstimate = exponentEstimate;
262 quotient = cast<uint01>(dr / ds);
263 lib_assert(quotient < 10, "quotient cannot be > 9
");
264 return correctedEstimate;
275 template<class t_type>
276 constexpr static void writeInt(String& string, t_type initial_value)
278 t_type value = initial_value;
279 constexpr uint04 max_digits = MaxDigits<t_type>() + (ObjectInfo<t_type>::Unsigned ? 0 : 1U);
280 char digits[max_digits];
281 uint04 index = max_digits;
282 if constexpr (!ObjectInfo<t_type>::Unsigned)
284 if (initial_value < cast<t_type>(0))
285 value = cast<t_type>(0) - value;
295 value = value / cast<t_type>(10);
296 j -= (value * cast<t_type>(10));
297 digits[--index] = (cast<char>(j) + '0');
298 if (value == cast<t_type>(0))
301 if constexpr ((!ObjectInfo<t_type>::Unsigned))
303 if(initial_value < cast<t_type>(0))
304 digits[--index] = '-';
306 string.addAll(&digits[index], max_digits - index);
308 template<class t_type>
309 constexpr static void writeHex(String& string, t_type initial_value)
311 t_type value = initial_value;
312 constexpr uint04 max_digits = MaxDigits<t_type>() + (ObjectInfo<t_type>::Unsigned ? 0 : 1U);
313 char digits[max_digits];
314 uint04 index = max_digits;
315 if ((!ObjectInfo<t_type>::Unsigned) && initial_value < cast<t_type>(0))
317 value = cast<t_type>(0) - value;
327 value = value / cast<t_type>(16);
328 j -= (value * cast<t_type>(16));
331 digits[--index] = '0' + cast<char>(j);
336 case 10: digits[--index] = 'A'; break;
337 case 11: digits[--index] = 'B'; break;
338 case 12: digits[--index] = 'C'; break;
339 case 13: digits[--index] = 'D'; break;
340 case 14: digits[--index] = 'E'; break;
341 case 15: digits[--index] = 'F'; break;
344 if (value == cast<t_type>(0))
347 string.addAll(&digits[index], max_digits - index);
350 static void writeFloat(String& string, fltp08 value, flt_to_string mode = DTOSTR_NORMAL, uint01 precision = 10)
352 if (value > Constant<fltp08>::Max)
354 string.addAll("-Infinity
");
357 if (value < Constant<fltp08>::Min)
359 string.addAll("Infinity
");
362 if (IsInvalid(value))
364 string.addAll("NaN
");
367 if (mode == DTOSTR_NORMAL)
369 sint08 intValue = cast<sint08>(value);
370 if ((value == (double)(intValue)) && ((uint32_t)intValue != 0x80000000))
372 writeInt(string, intValue);
376 const bool negative = value < 0.0;
377 const bool zero = value == 0.0;
382 D2A d2a = D2A(value, mode != DTOSTR_NORMAL, precision);
383 int32_t exp10 = d2a.expBase10() - 1;
397 format = kFixedFraction;
405 case DTOSTR_PRECISION:
410 else if (exp10 >= precision) {
411 format = kExponential;
418 case DTOSTR_EXPONENTIAL:
419 format = kExponential;
423 if (exp10 < 0 && exp10 > -7) {
424 // Number is of form 0.######
425 if (exp10 < -precision) {
426 exp10 = -precision - 1;
430 else if (std::abs(exp10) > 20) { // ECMA spec 9.8.1
431 format = kExponential;
444 digit = d2a.nextDigit();
446 string.add((char)(digit + '0'));
449 digit = (d2a.finished) ? 0 : d2a.nextDigit();
450 string.add((char)(digit + '0'));
454 if (mode == DTOSTR_FIXED)
458 if (mode == DTOSTR_NORMAL)
463 while (!d2a.finished)
464 string.add((char)(d2a.nextDigit() + '0'));
467 else if (digits < precision - 1)
470 for (; digits < precision - 1; digits++)
472 digit = d2a.finished ? 0 : d2a.nextDigit();
473 string.add((char)(digit + '0'));
480 string.add('0'); // Sentinel
482 // Write out leading zeroes
485 while (++exp10 < 10 && digits < precision) {
490 else if (exp10 < 0) {
491 while ((++exp10 < 0) && (precision-- > 0))
494 if (!IsInvalid(precision))
496 // Write out significand
497 for (; digits < precision; digits++) {
500 if (mode == DTOSTR_NORMAL)
505 string.add((char)(d2a.nextDigit() + '0'));
514 string.add('0'); // Sentinel
517 // Write out leading zeros
520 for (int32_t i = exp10; i < -1; i++) {
525 // Write out significand
527 while (!d2a.finished)
529 string.add((char)(d2a.nextDigit() + '0'));
530 if (mode != DTOSTR_NORMAL && ++i >= precision)
533 if (mode == DTOSTR_PRECISION)
535 while (i++ < precision)
536 string.add((char)(d2a.finished ? '0' : d2a.nextDigit() + '0'));
544 digit = d2a.finished ? 0 : d2a.nextDigit();
545 string.add((char)(digit + '0'));
546 if (((mode == DTOSTR_NORMAL) && !d2a.finished) ||
547 ((mode != DTOSTR_NORMAL) && precision > 1)) {
549 for (int32_t i = 0; i < precision - 1; i++) {
552 if (mode == DTOSTR_NORMAL)
557 string.add((char)(d2a.nextDigit() + '0'));
564 // Clean up zeroes, and place the exponent
569 // Remove trailing zeroes, as might appear in 10e+95
570 while (string.size() > 0 && string.last() == '0')
577 // Place the exponent
582 writeInt(string, exp10);
#define lib_assert(expression, message)
Definition LibAssert.h:61
Logic for writing to a number.
Definition NumberWriter.h:50
uint8_t uint01
-Defines an alias representing a 1 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:80
uint64_t uint08
-Defines an alias representing an 8 byte, unsigned integer
Definition BaseValues.hpp:106
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:96
constexpr t_to cast(const Angle< t_from > &value)
Definition Angle.h:375
double fltp08
Defines an alias representing an 8 byte floating-point number.
Definition BaseValues.hpp:149
Defines for a given type (such as sint04, fltp08, UUID, etc) a maximum, minimum, and reserved 'invali...
Definition BaseValues.hpp:233