static int d2s_buffered_n(double f, Span <char> result) { // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. ulong bits = double_to_bits(f); // Decode bits into sign, mantissa, and exponent. bool ieeeSign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; ulong ieeeMantissa = bits & ((1ul << DOUBLE_MANTISSA_BITS) - 1); uint ieeeExponent = (uint)((bits >> DOUBLE_MANTISSA_BITS) & ((1u << DOUBLE_EXPONENT_BITS) - 1)); // Case distinction; exit early for the easy cases. if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) { return(copy_special_str(result, ieeeSign, Convert.ToBoolean(ieeeExponent), Convert.ToBoolean(ieeeMantissa))); } floating_decimal_64 v = default(floating_decimal_64); bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, ref v); if (isSmallInt) { // For small integers in the range [1, 2^53), v.mantissa might contain trailing (decimal) zeros. // For scientific notation we need to move these zeros into the exponent. // (This is not needed for fixed-point notation, so it might be beneficial to trim // trailing zeros in to_chars only if needed - once fixed-point notation output is implemented.) for (; ;) { ulong q = div10(v.mantissa); uint r = ((uint)v.mantissa) - 10 * ((uint)q); if (r != 0) { break; } v.mantissa = q; ++v.exponent; } } else { v = d2d(ieeeMantissa, ieeeExponent); } return(to_chars(v, ieeeSign, result)); }
static bool d2d_small_int(ulong ieeeMantissa, uint ieeeExponent, ref floating_decimal_64 v) { ulong m2 = (1ul << DOUBLE_MANTISSA_BITS) | ieeeMantissa; int e2 = (int)ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; if (e2 > 0) { // f = m2 * 2^e2 >= 2^53 is an integer. // Ignore this case for now. return(false); } if (e2 < -52) { // f < 1. return(false); } // Since 2^52 <= m2 < 2^53 and 0 <= -e2 <= 52: 1 <= f = m2 / 2^-e2 < 2^53. // Test if the lower -e2 bits of the significand are 0, i.e. whether the fraction is 0. ulong mask = (1ul << -e2) - 1; ulong fraction = m2 & mask; if (fraction != 0) { return(false); } // f is an integer in the range [1, 2^53). // Note: mantissa might contain trailing (decimal) 0's. // Note: since 2^53 < 10^16, there is no need to adjust decimalLength17(). v.mantissa = m2 >> -e2; v.exponent = 0; return(true); }
static int to_chars(floating_decimal_64 v, bool sign, Span <char> result) { // Step 5: Print the decimal representation. int index = 0; if (sign) { result[index++] = '-'; } ulong output = v.mantissa; int olength = decimalLength17(output); // Print the decimal digits. // The following code is equivalent to: // for (uint i = 0; i < olength - 1; ++i) { // uint c = output % 10; output /= 10; // result[index + olength - i] = (char) ('0' + c); // } // result[index] = '0' + output % 10; int i = 0; // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint can store 9 digits. // If output doesn't fit into uint, we cut off 8 digits, // so the rest will fit into uint. uint output2; if ((output >> 32) != 0) { // Expensive 64-bit division. ulong q = div1e8(output); output2 = ((uint)output) - 100000000 * ((uint)q); output = q; uint c = output2 % 10000; output2 /= 10000; uint d = output2 % 10000; uint c0 = (c % 100) << 1; uint c1 = (c / 100) << 1; uint d0 = (d % 100) << 1; uint d1 = (d / 100) << 1; memcpy(result.Slice(index + olength - i - 1), DIGIT_TABLE, c0, 2); memcpy(result.Slice(index + olength - i - 3), DIGIT_TABLE, c1, 2); memcpy(result.Slice(index + olength - i - 5), DIGIT_TABLE, d0, 2); memcpy(result.Slice(index + olength - i - 7), DIGIT_TABLE, d1, 2); i += 8; } output2 = (uint)output; while (output2 >= 10000) { uint c = output2 % 10000; output2 /= 10000; uint c0 = (c % 100) << 1; uint c1 = (c / 100) << 1; memcpy(result.Slice(index + olength - i - 1), DIGIT_TABLE, c0, 2); memcpy(result.Slice(index + olength - i - 3), DIGIT_TABLE, c1, 2); i += 4; } if (output2 >= 100) { uint c = (output2 % 100) << 1; output2 /= 100; memcpy(result.Slice(index + olength - i - 1), DIGIT_TABLE, c, 2); i += 2; } if (output2 >= 10) { uint c = output2 << 1; // We can't use memcpy here: the decimal dot goes between these two digits. result[index + olength - i] = DIGIT_TABLE[c + 1]; result[index] = DIGIT_TABLE[c]; } else { result[index] = (char)('0' + output2); } // Print decimal point if needed. if (olength > 1) { result[index + 1] = '.'; index += olength + 1; } else { ++index; } // Print the exponent. result[index++] = 'E'; int exp = v.exponent + (int)olength - 1; if (exp < 0) { result[index++] = '-'; exp = -exp; } if (exp >= 100) { int c = exp % 10; memcpy(result.Slice(index), DIGIT_TABLE, 2 * (exp / 10), 2); result[index + 2] = (char)('0' + c); index += 3; } else if (exp >= 10) { memcpy(result.Slice(index), DIGIT_TABLE, 2 * exp, 2); index += 2; } else { result[index++] = (char)('0' + exp); } return(index); }