private static bool DoubleToShortestAscii(ref GrisuDouble v, Span <char> buffer, out int length, out int point) { Debug.Assert(!v.IsSpecial); Debug.Assert(v.Value >= 0.0); double value = v.Value; if (value == 0.0) { buffer[0] = '0'; buffer[1] = '\0'; length = 1; point = 1; return(true); } int decimal_exponent; bool result = Grisu3(ref v, buffer, out length, out decimal_exponent); if (result) { point = length + decimal_exponent; } else { point = 0; } return(result); }
private static void HandleSpecialValues( ref GrisuDouble double_inspect, TextWriter writer) { if (double_inspect.IsInfinite) { if (double_inspect.Value < 0) { writer.Write('-'); } writer.Write(infinity_symbol_); return; } if (double_inspect.IsNaN) { writer.Write(nan_symbol_); return; } }
internal unsafe static string TryFormat(double value) { Span <char> resultBuf = stackalloc char[24]; var ssb = new StackStringBuilder(resultBuf); if (value < 0.0) { ssb.Append('-'); value = -value; } GrisuDouble grisuDouble = new GrisuDouble(value); if (grisuDouble.IsSpecial) { return(null); } Span <char> decimal_rep = stackalloc char[18]; int decimal_point; int decimal_rep_length; if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point)) { return(null); } int decimalRepLength = decimal_rep_length; if (decimal_point < 1) { decimalRepLength += -decimal_point + 1; } else if (decimal_point >= decimal_rep_length) { decimalRepLength += decimal_point - decimal_rep_length + 1; } int exponent = decimal_point - 1; int absExponent = Math.Abs(exponent); int exponentRepLength = decimal_rep_length + 3; if (exponent < 0) { ++exponentRepLength; } if (absExponent >= 10) { ++exponentRepLength; if (absExponent >= 100) { ++exponentRepLength; } } if (decimalRepLength <= exponentRepLength) { CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, Math.Max(0, decimal_rep_length - decimal_point), ssb); } else { CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, ssb); } return(ssb.ToString()); }
// Provides a decimal representation of v. // Returns true if it succeeds, otherwise the result cannot be trusted. // There will be *length digits inside the buffer (not null-terminated). // If the function returns true then // v == (double) (buffer * 10^decimal_exponent). // The digits in the buffer are the shortest representation possible: no // 0.09999999999999999 instead of 0.1. The shorter representation will even be // chosen even if the longer one would be closer to v. // The last digit will be closest to the actual v. That is, even if several // digits might correctly yield 'v' when read again, the closest will be // computed. private static bool Grisu3(ref GrisuDouble v, Span <char> buffer, out int length, out int decimal_exponent) { DiyFp w = v.AsNormalizedDiyFp(); // boundary_minus and boundary_plus are the boundaries between v and its // closest floating-point neighbors. Any number strictly between // boundary_minus and boundary_plus will round to v when convert to a double. // Grisu3 will never output representations that lie exactly on a boundary. DiyFp boundary_minus, boundary_plus; v.NormalizedBoundaries(out boundary_minus, out boundary_plus); Debug.Assert(boundary_plus.E == w.E); DiyFp ten_mk; // Cached power of ten: 10^-k int mk; // -k int ten_mk_minimal_binary_exponent = kMinimalTargetExponent - (w.E + DiyFp.kSignificandSize); int ten_mk_maximal_binary_exponent = kMaximalTargetExponent - (w.E + DiyFp.kSignificandSize); PowersOfTenCache.GetCachedPowerForBinaryExponentRange( ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, out ten_mk, out mk); Debug.Assert((kMinimalTargetExponent <= w.E + ten_mk.E + DiyFp.kSignificandSize) && (kMaximalTargetExponent >= w.E + ten_mk.E + DiyFp.kSignificandSize)); // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a // 64 bit significand and ten_mk is thus only precise up to 64 bits. // The DiyFp.Times procedure rounds its result, and ten_mk is approximated // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now // off by a small amount. // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. // In other words: let f = scaled_w.f() and e = scaled_w.e(), then // (f-1) * 2^e < w*10^k < (f+1) * 2^e //DiyFp scaled_w = DiyFp.Times(ref w, ref ten_mk); w.Multiply(ref ten_mk); Debug.Assert(w.E == boundary_plus.E + ten_mk.E + DiyFp.kSignificandSize); // In theory it would be possible to avoid some recomputations by computing // the difference between w and boundary_minus/plus (a power of 2) and to // compute scaled_boundary_minus/plus by subtracting/adding from // scaled_w. However the code becomes much less readable and the speed // enhancements are not terriffic. //DiyFp scaled_boundary_minus = DiyFp.Times(ref boundary_minus, ref ten_mk); boundary_minus.Multiply(ref ten_mk); //DiyFp scaled_boundary_plus = DiyFp.Times(ref boundary_plus, ref ten_mk); boundary_plus.Multiply(ref ten_mk); // DigitGen will generate the digits of scaled_w. Therefore we have // v == (double) (scaled_w * 10^-mk). // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an // integer than it will be updated. For instance if scaled_w == 1.23 then // the buffer will be filled with "123" und the decimal_exponent will be // decreased by 2. int kappa; bool result = DigitGen(ref boundary_minus, ref w, ref boundary_plus, buffer, out length, out kappa); decimal_exponent = -mk + kappa; return(result); }
public static void DoubleToString(double value, TextWriter writer) { if (value < 0.0) { writer.Write('-'); value = -value; } GrisuDouble grisuDouble = new GrisuDouble(value); if (grisuDouble.IsSpecial) { HandleSpecialValues(ref grisuDouble, writer); return; } char[] decimal_rep = ts_decimal_rep; if (decimal_rep == null) decimal_rep = ts_decimal_rep = new char[kBase10MaximalLength + 1]; int decimal_point; int decimal_rep_length; if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point)) { writer.Write("{0:R}", value); return; } int decimalRepLength = decimal_rep_length; if (decimal_point < 1) { decimalRepLength += -decimal_point + 1; } else if (decimal_point >= decimal_rep_length) { decimalRepLength += decimal_point - decimal_rep_length + 1; } int exponent = decimal_point - 1; int absExponent = Math.Abs(exponent); int exponentRepLength = decimal_rep_length + 3; if (exponent < 0) ++exponentRepLength; if (absExponent >= 10) { ++exponentRepLength; if (absExponent >= 100) ++exponentRepLength; } if (decimalRepLength <= exponentRepLength) { CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, Math.Max(0, decimal_rep_length - decimal_point), writer); } else { CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, writer); } }
// Provides a decimal representation of v. // Returns true if it succeeds, otherwise the result cannot be trusted. // There will be *length digits inside the buffer (not null-terminated). // If the function returns true then // v == (double) (buffer * 10^decimal_exponent). // The digits in the buffer are the shortest representation possible: no // 0.09999999999999999 instead of 0.1. The shorter representation will even be // chosen even if the longer one would be closer to v. // The last digit will be closest to the actual v. That is, even if several // digits might correctly yield 'v' when read again, the closest will be // computed. private static bool Grisu3(ref GrisuDouble v, char[] buffer, out int length, out int decimal_exponent) { DiyFp w = v.AsNormalizedDiyFp(); // boundary_minus and boundary_plus are the boundaries between v and its // closest floating-point neighbors. Any number strictly between // boundary_minus and boundary_plus will round to v when convert to a double. // Grisu3 will never output representations that lie exactly on a boundary. DiyFp boundary_minus, boundary_plus; v.NormalizedBoundaries(out boundary_minus, out boundary_plus); Debug.Assert(boundary_plus.E == w.E); DiyFp ten_mk; // Cached power of ten: 10^-k int mk; // -k int ten_mk_minimal_binary_exponent = kMinimalTargetExponent - (w.E + DiyFp.kSignificandSize); int ten_mk_maximal_binary_exponent = kMaximalTargetExponent - (w.E + DiyFp.kSignificandSize); PowersOfTenCache.GetCachedPowerForBinaryExponentRange( ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, out ten_mk, out mk); Debug.Assert((kMinimalTargetExponent <= w.E + ten_mk.E + DiyFp.kSignificandSize) && (kMaximalTargetExponent >= w.E + ten_mk.E + DiyFp.kSignificandSize)); // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a // 64 bit significand and ten_mk is thus only precise up to 64 bits. // The DiyFp.Times procedure rounds its result, and ten_mk is approximated // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now // off by a small amount. // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. // In other words: let f = scaled_w.f() and e = scaled_w.e(), then // (f-1) * 2^e < w*10^k < (f+1) * 2^e //DiyFp scaled_w = DiyFp.Times(ref w, ref ten_mk); w.Multiply(ref ten_mk); Debug.Assert(w.E == boundary_plus.E + ten_mk.E + DiyFp.kSignificandSize); // In theory it would be possible to avoid some recomputations by computing // the difference between w and boundary_minus/plus (a power of 2) and to // compute scaled_boundary_minus/plus by subtracting/adding from // scaled_w. However the code becomes much less readable and the speed // enhancements are not terriffic. //DiyFp scaled_boundary_minus = DiyFp.Times(ref boundary_minus, ref ten_mk); boundary_minus.Multiply(ref ten_mk); //DiyFp scaled_boundary_plus = DiyFp.Times(ref boundary_plus, ref ten_mk); boundary_plus.Multiply(ref ten_mk); // DigitGen will generate the digits of scaled_w. Therefore we have // v == (double) (scaled_w * 10^-mk). // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an // integer than it will be updated. For instance if scaled_w == 1.23 then // the buffer will be filled with "123" und the decimal_exponent will be // decreased by 2. int kappa; bool result = DigitGen(ref boundary_minus, ref w, ref boundary_plus, buffer, out length, out kappa); decimal_exponent = -mk + kappa; return result; }
private static bool DoubleToShortestAscii(ref GrisuDouble v, char[] buffer, out int length, out int point) { Debug.Assert(!v.IsSpecial); Debug.Assert(v.Value >= 0.0); double value = v.Value; if (value == 0.0) { buffer[0] = '0'; buffer[1] = '\0'; length = 1; point = 1; return true; } int decimal_exponent; bool result = Grisu3(ref v, buffer, out length, out decimal_exponent); if (result) { point = length + decimal_exponent; } else { point = 0; } return result; }
private static void HandleSpecialValues( ref GrisuDouble double_inspect, TextWriter writer) { if (double_inspect.IsInfinite) { if (double_inspect.Value < 0) { writer.Write('-'); } writer.Write(infinity_symbol_); return; } if (double_inspect.IsNaN) { writer.Write(nan_symbol_); return; } }
public static void DoubleToString(double value, TextWriter writer) { if (value < 0.0) { writer.Write('-'); value = -value; } GrisuDouble grisuDouble = new GrisuDouble(value); if (grisuDouble.IsSpecial) { HandleSpecialValues(ref grisuDouble, writer); return; } char[] decimal_rep = ts_decimal_rep; if (decimal_rep == null) { decimal_rep = ts_decimal_rep = new char[kBase10MaximalLength + 1]; } int decimal_point; int decimal_rep_length; if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point)) { writer.Write(string.Format(CultureInfo.InvariantCulture, "{0:R}", value)); return; } int decimalRepLength = decimal_rep_length; if (decimal_point < 1) { decimalRepLength += -decimal_point + 1; } else if (decimal_point >= decimal_rep_length) { decimalRepLength += decimal_point - decimal_rep_length; } int exponent = decimal_point - 1; int absExponent = Math.Abs(exponent); int exponentRepLength = decimal_rep_length + 2; // one for 'e' and one for first exponent digit if (exponent < 0) { ++exponentRepLength; // additional one for '-' } if (absExponent >= 10) { ++exponentRepLength; // additional one for second exponent digit if (absExponent >= 100) { ++exponentRepLength; // additional one for third exponent digit } } if (decimalRepLength <= exponentRepLength) { CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, Math.Max(0, decimal_rep_length - decimal_point), writer); } else { CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, writer); } }
public static void DoubleToString(double value, TextWriter writer) { if (value < 0.0) { writer.Write('-'); value = -value; } GrisuDouble grisuDouble = new GrisuDouble(value); if (grisuDouble.IsSpecial) { HandleSpecialValues(ref grisuDouble, writer); return; } char[] decimal_rep = ts_decimal_rep; if (decimal_rep == null) { decimal_rep = ts_decimal_rep = new char[kBase10MaximalLength + 1]; } int decimal_point; int decimal_rep_length; if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point)) { writer.Write("{0:R}", value); return; } int decimalRepLength = decimal_rep_length; if (decimal_point < 1) { decimalRepLength += -decimal_point + 1; } else if (decimal_point >= decimal_rep_length) { decimalRepLength += decimal_point - decimal_rep_length + 1; } int exponent = decimal_point - 1; int absExponent = Math.Abs(exponent); int exponentRepLength = decimal_rep_length + 3; if (exponent < 0) { ++exponentRepLength; } if (absExponent >= 10) { ++exponentRepLength; if (absExponent >= 100) { ++exponentRepLength; } } if (decimalRepLength <= exponentRepLength) { CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, Math.Max(0, decimal_rep_length - decimal_point), writer); } else { CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, writer); } }