Description
During implementation of BFloat16
, I examined the parsing/formatting traits of other FP types and found HalfNumberBufferLength
is incorrect.
runtime/src/libraries/Common/src/System/Number.NumberBuffer.cs
Lines 14 to 23 in 52c14fb
Calculation
The buffer length is decided by highest possible significant digits of the type. Such value occurs when setting BiasedExponent to 1 and TrailingSignificand to all bits set. For Half it's 0x07FF
.
Let e = Abs(MinExponent) = ExponentBias - 1
and m = TrailingSignificandLength
,
Significand of the value should be (2 - 2^-m)
, and exponent should be -e
, so the value is (2 - 2^-m) * 2^-e
Convert the value to fractional: (2^(m+1) - 1) / (2^(e+m))
Multiply 5^(e+m)
to both numerator and denominator to get decimal fraction: (2^(m+1) - 1)*5^(e+m) / 10^(e+m)
The numerator won't contain trailing 0, so the total significand digits of the fraction is the magnitude of its numerator:
(2^(m+1) - 1)*5^(e+m) ≈ 2^(m+1) * 5^(e+m) = 10^(m+1) * 5^(e-1)
So the total significand digits is m+1+Log10(5^(e-1)) = m+1+(e-1)*Log10(5)
(ceiling)
I'm going to add comment for this together with BFloat16.
For double, e = 1022, m = 52, total digits = 766.6483744270753
For float, e = 126, m = 23, total digits = 111.37125054200236
For Half, e = 14, m = 10, total digits = 20.086610056368244
So the longest Half value has 21 significant digits.
Convert the value to double and validate, it's 0.000122010707855224609375
.
Observation
BitConverter.UInt16BitsToHalf(0x0x07FF).ToString(99)
throws IndexOutOfRangeException.
This also reproduces in .NET 6, probably present since the introduction of Half
. We should fix and backport it, at least to 8.0.
The file has been refactored so it may require manual backport.