Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Double.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,33 +354,33 @@ public override int GetHashCode()

public override string ToString()
{
return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo);
return Number.FormatFloat(m_value, null, NumberFormatInfo.CurrentInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not going to block on this, but we should probably call this FormatFloatingPoint instead and it would be good to fix it in a follow up PR.

Float is going to be confused with the C# keyword float, which should be called Single in the BCL API surface area

}

public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
{
return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo);
return Number.FormatFloat(m_value, format, NumberFormatInfo.CurrentInfo);
}

public string ToString(IFormatProvider? provider)
{
return Number.FormatDouble(m_value, null, NumberFormatInfo.GetInstance(provider));
return Number.FormatFloat(m_value, null, NumberFormatInfo.GetInstance(provider));
}

public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
{
return Number.FormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider));
return Number.FormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider));
}

public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
}

/// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten);
return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten);
}

public static double Parse(string s) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null);
Expand Down Expand Up @@ -2335,6 +2335,10 @@ public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFo

static ulong IBinaryFloatParseAndFormatInfo<double>.FloatToBits(double value) => BitConverter.DoubleToUInt64Bits(value);

static int IBinaryFloatParseAndFormatInfo<double>.MaxRoundTripDigits => 17;

static int IBinaryFloatParseAndFormatInfo<double>.MaxPrecisionCustomFormat => 15;

//
// Helpers
//
Expand Down
16 changes: 10 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Half.cs
Original file line number Diff line number Diff line change
Expand Up @@ -505,31 +505,31 @@ public override int GetHashCode()
/// </summary>
public override string ToString()
{
return Number.FormatHalf(this, null, NumberFormatInfo.CurrentInfo);
return Number.FormatFloat(this, null, NumberFormatInfo.CurrentInfo);
}

/// <summary>
/// Returns a string representation of the current value using the specified <paramref name="format"/>.
/// </summary>
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
{
return Number.FormatHalf(this, format, NumberFormatInfo.CurrentInfo);
return Number.FormatFloat(this, format, NumberFormatInfo.CurrentInfo);
}

/// <summary>
/// Returns a string representation of the current value with the specified <paramref name="provider"/>.
/// </summary>
public string ToString(IFormatProvider? provider)
{
return Number.FormatHalf(this, null, NumberFormatInfo.GetInstance(provider));
return Number.FormatFloat(this, null, NumberFormatInfo.GetInstance(provider));
}

/// <summary>
/// Returns a string representation of the current value using the specified <paramref name="format"/> and <paramref name="provider"/>.
/// </summary>
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
{
return Number.FormatHalf(this, format, NumberFormatInfo.GetInstance(provider));
return Number.FormatFloat(this, format, NumberFormatInfo.GetInstance(provider));
}

/// <summary>
Expand All @@ -542,13 +542,13 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin
/// <returns></returns>
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
}

/// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten);
return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten);
}

//
Expand Down Expand Up @@ -2373,5 +2373,9 @@ public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFo
static Half IBinaryFloatParseAndFormatInfo<Half>.BitsToFloat(ulong bits) => BitConverter.UInt16BitsToHalf((ushort)(bits));

static ulong IBinaryFloatParseAndFormatInfo<Half>.FloatToBits(Half value) => BitConverter.HalfToUInt16Bits(value);

static int IBinaryFloatParseAndFormatInfo<Half>.MaxRoundTripDigits => 5;

static int IBinaryFloatParseAndFormatInfo<Half>.MaxPrecisionCustomFormat => 5;
}
}
63 changes: 10 additions & 53 deletions src/libraries/System.Private.CoreLib/src/System/Number.DiyFp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ internal static partial class Number
// DiyFp are not designed to contain special doubles (NaN and Infinity).
internal readonly ref struct DiyFp
{
public const int DoubleImplicitBitIndex = 52;
public const int SingleImplicitBitIndex = 23;
public const int HalfImplicitBitIndex = 10;

public const int SignificandSize = 64;

public readonly ulong f;
Expand All @@ -33,60 +29,21 @@ internal readonly ref struct DiyFp
//
// Precondition:
// The value encoded by value must be greater than 0.
public static DiyFp CreateAndGetBoundaries(double value, out DiyFp mMinus, out DiyFp mPlus)
public static DiyFp CreateAndGetBoundaries<TNumber>(TNumber value, out DiyFp mMinus, out DiyFp mPlus)
where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo<TNumber>
{
var result = new DiyFp(value);
result.GetBoundaries(DoubleImplicitBitIndex, out mMinus, out mPlus);
var result = Create(value);
result.GetBoundaries(TNumber.DenormalMantissaBits, out mMinus, out mPlus);
return result;
}

// Computes the two boundaries of value.
//
// The bigger boundary (mPlus) is normalized.
// The lower boundary has the same exponent as mPlus.
//
// Precondition:
// The value encoded by value must be greater than 0.
public static DiyFp CreateAndGetBoundaries(float value, out DiyFp mMinus, out DiyFp mPlus)
{
var result = new DiyFp(value);
result.GetBoundaries(SingleImplicitBitIndex, out mMinus, out mPlus);
return result;
}

// Computes the two boundaries of value.
//
// The bigger boundary (mPlus) is normalized.
// The lower boundary has the same exponent as mPlus.
//
// Precondition:
// The value encoded by value must be greater than 0.
public static DiyFp CreateAndGetBoundaries(Half value, out DiyFp mMinus, out DiyFp mPlus)
{
var result = new DiyFp(value);
result.GetBoundaries(HalfImplicitBitIndex, out mMinus, out mPlus);
return result;
}

public DiyFp(double value)
{
Debug.Assert(double.IsFinite(value));
Debug.Assert(value > 0.0);
f = ExtractFractionAndBiasedExponent(value, out e);
}

public DiyFp(float value)
{
Debug.Assert(float.IsFinite(value));
Debug.Assert(value > 0.0f);
f = ExtractFractionAndBiasedExponent(value, out e);
}

public DiyFp(Half value)
public static DiyFp Create<TNumber>(TNumber value)
where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo<TNumber>
{
Debug.Assert(Half.IsFinite(value));
Debug.Assert((float)value > 0.0f);
f = ExtractFractionAndBiasedExponent(value, out e);
Debug.Assert(TNumber.IsFinite(value));
Debug.Assert(value > TNumber.Zero);
ulong f = ExtractFractionAndBiasedExponent(value, out int e);
return new DiyFp(f, e);
}

public DiyFp(ulong f, int e)
Expand Down
75 changes: 8 additions & 67 deletions src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,82 +10,23 @@ namespace System
// The backing algorithm and the proofs behind it are described in more detail here: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
internal static partial class Number
{
public static void Dragon4Double(double value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
public static unsafe void Dragon4<TNumber>(TNumber value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo<TNumber>
{
double v = double.IsNegative(value) ? -value : value;
TNumber v = TNumber.IsNegative(value) ? -value : value;

Debug.Assert(v > 0);
Debug.Assert(double.IsFinite(v));
Debug.Assert(v > TNumber.Zero);
Debug.Assert(TNumber.IsFinite(v));

ulong mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);

uint mantissaHighBitIdx;
bool hasUnequalMargins = false;

if ((mantissa >> DiyFp.DoubleImplicitBitIndex) != 0)
if ((mantissa >> TNumber.DenormalMantissaBits) != 0)
{
mantissaHighBitIdx = DiyFp.DoubleImplicitBitIndex;
hasUnequalMargins = (mantissa == (1UL << DiyFp.DoubleImplicitBitIndex));
}
else
{
Debug.Assert(mantissa != 0);
mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa);
}

int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent));

number.Scale = decimalExponent + 1;
number.Digits[length] = (byte)('\0');
number.DigitsCount = length;
}

public static unsafe void Dragon4Half(Half value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
{
Half v = Half.IsNegative(value) ? Half.Negate(value) : value;

Debug.Assert((double)v > 0.0);
Debug.Assert(Half.IsFinite(v));

ushort mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);

uint mantissaHighBitIdx;
bool hasUnequalMargins = false;

if ((mantissa >> DiyFp.HalfImplicitBitIndex) != 0)
{
mantissaHighBitIdx = DiyFp.HalfImplicitBitIndex;
hasUnequalMargins = (mantissa == (1U << DiyFp.HalfImplicitBitIndex));
}
else
{
Debug.Assert(mantissa != 0);
mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa);
}

int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent));

number.Scale = decimalExponent + 1;
number.Digits[length] = (byte)('\0');
number.DigitsCount = length;
}

public static unsafe void Dragon4Single(float value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
{
float v = float.IsNegative(value) ? -value : value;

Debug.Assert(v > 0);
Debug.Assert(float.IsFinite(v));

uint mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);

uint mantissaHighBitIdx;
bool hasUnequalMargins = false;

if ((mantissa >> DiyFp.SingleImplicitBitIndex) != 0)
{
mantissaHighBitIdx = DiyFp.SingleImplicitBitIndex;
hasUnequalMargins = (mantissa == (1U << DiyFp.SingleImplicitBitIndex));
mantissaHighBitIdx = TNumber.DenormalMantissaBits;
hasUnequalMargins = (mantissa == (1U << TNumber.DenormalMantissaBits));
}
else
{
Expand Down
Loading