Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Updating Single/Double to always roundtrip to the correct number of d…
…igits.
  • Loading branch information
tannergooding committed Sep 12, 2018
1 parent 7ee5d53 commit 7ef4638
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 99 deletions.
116 changes: 20 additions & 96 deletions src/System.Private.CoreLib/shared/System/Number.Formatting.cs
Expand Up @@ -243,8 +243,8 @@ namespace System
internal static partial class Number
{
internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value
private const int FloatPrecision = 7;
private const int DoublePrecision = 15;
private const int SinglePrecision = 9;
private const int DoublePrecision = 17;
private const int ScaleNAN = unchecked((int)0x80000000);
private const int ScaleINF = 0x7FFFFFFF;
private const int MaxUInt32DecDigits = 10;
Expand Down Expand Up @@ -391,57 +391,18 @@ private static string FormatDouble(ref ValueStringBuilder sb, double value, Read
NumberBuffer number = default;
number.kind = NumberBufferKind.Double;

switch (fmt)
if ((fmt == 'R') || (fmt == 'r'))
{
case 'R':
case 'r':
{
// In order to give numbers that are both friendly to display and round-trippable, we parse the
// number using 15 digits and then determine if it round trips to the same value. If it does, we
// convert that NUMBER to a string, otherwise we reparse using 17 digits and display that.
DoubleToNumber(value, DoublePrecision, ref number);
if (number.scale == ScaleNAN)
{
return info.NaNSymbol;
}
else if (number.scale == ScaleINF)
{
return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
}

if (NumberToDouble(ref number) == value)
{
NumberToString(ref sb, ref number, 'G', DoublePrecision, info);
}
else
{
DoubleToNumber(value, 17, ref number);
NumberToString(ref sb, ref number, 'G', 17, info);
}

return null;
}

case 'E':
case 'e':
// Round values less than E14 to 15 digits
if (digits > 14)
{
precision = 17;
}
break;

case 'G':
case 'g':
// Round values less than G15 to 15 digits. G16 and G17 will not be touched.
if (digits > 15)
{
precision = 17;
}
break;
fmt = 'G';
digits = DoublePrecision;
}
else if (digits != -1)
{
precision = digits;
}

DoubleToNumber(value, precision, ref number);

if (number.scale == ScaleNAN)
{
return info.NaNSymbol;
Expand Down Expand Up @@ -488,60 +449,22 @@ public static bool TryFormatSingle(float value, ReadOnlySpan<char> format, Numbe
private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan<char> format, NumberFormatInfo info)
{
char fmt = ParseFormatSpecifier(format, out int digits);
int precision = FloatPrecision;
int precision = SinglePrecision;
NumberBuffer number = default;
number.kind = NumberBufferKind.Double;

switch (fmt)
if ((fmt == 'R') || (fmt == 'r'))
{
case 'R':
case 'r':
{
// In order to give numbers that are both friendly to display and round-trippable, we parse the
// number using 7 digits and then determine if it round trips to the same value. If it does, we
// convert that NUMBER to a string, otherwise we reparse using 9 digits and display that.
DoubleToNumber(value, FloatPrecision, ref number);
if (number.scale == ScaleNAN)
{
return info.NaNSymbol;
}
else if (number.scale == ScaleINF)
{
return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
}

if ((float)NumberToDouble(ref number) == value)
{
NumberToString(ref sb, ref number, 'G', FloatPrecision, info);
}
else
{
DoubleToNumber(value, 9, ref number);
NumberToString(ref sb, ref number, 'G', 9, info);
}
return null;
}

case 'E':
case 'e':
// Round values less than E14 to 15 digits.
if (digits > 6)
{
precision = 9;
}
break;

case 'G':
case 'g':
// Round values less than G15 to 15 digits. G16 and G17 will not be touched.
if (digits > 7)
{
precision = 9;
}
break;
fmt = 'G';
digits = SinglePrecision;
}
else
{
precision = digits;
}

DoubleToNumber(value, precision, ref number);

if (number.scale == ScaleNAN)
{
return info.NaNSymbol;
Expand All @@ -559,6 +482,7 @@ private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadO
{
NumberToStringFormat(ref sb, ref number, format, info);
}

return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/System.Private.CoreLib/shared/System/Number.Parsing.cs
Expand Up @@ -534,7 +534,7 @@ private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles
{
number.scale = 0;
}
if ((state & StateDecimal) == 0)
if ((number.kind == NumberBufferKind.Integer) && ((state & StateDecimal) == 0))
{
number.sign = false;
}
Expand Down
11 changes: 9 additions & 2 deletions src/classlibnative/bcltype/number.cpp
Expand Up @@ -304,13 +304,20 @@ void DoubleToNumber(double value, int precision, NUMBER* number)
WRAPPER_NO_CONTRACT
_ASSERTE(number != NULL);

if (precision > NUMBER_MAXDIGITS)
{
precision = NUMBER_MAXDIGITS;
}
number->precision = precision;
if (((FPDOUBLE*)&value)->exp == 0x7FF) {

if (((FPDOUBLE*)&value)->exp == 0x7FF)
{
number->scale = (((FPDOUBLE*)&value)->mantLo || ((FPDOUBLE*)&value)->mantHi) ? SCALE_NAN: SCALE_INF;
number->sign = ((FPDOUBLE*)&value)->sign;
number->digits[0] = 0;
}
else {
else
{
DoubleToNumberWorker(value, precision, &number->scale, &number->sign, number->digits);
}
}
Expand Down

0 comments on commit 7ef4638

Please sign in to comment.