Skip to content

Commit d2d1fe5

Browse files
committed
fix: align finite printf and quoted numeric string semantics
1 parent 12d0ee2 commit d2d1fe5

8 files changed

Lines changed: 650 additions & 228 deletions

File tree

‎src/Lua/Internal/HexConverter.cs‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,85 @@ public static string FromDouble(double value)
143143
var mantissaHex = mantissa.ToString("x13").TrimEnd('0');
144144
return $"{sign}0x{leadingDigit}.{mantissaHex}p{adjustedExponent:+0;-0}";
145145
}
146+
147+
internal static string FormatDouble(
148+
double value,
149+
int precision,
150+
bool alternateForm,
151+
bool upperCase
152+
)
153+
{
154+
string text;
155+
if (double.IsNaN(value) || double.IsInfinity(value))
156+
{
157+
text = value.ToString(CultureInfo.InvariantCulture);
158+
}
159+
else
160+
{
161+
text = FormatFinite(value, precision, alternateForm);
162+
}
163+
return upperCase ? text.ToUpperInvariant() : text;
164+
}
165+
166+
static string FormatFinite(double value, int precision, bool alternateForm)
167+
{
168+
const ulong fractionMask = (1UL << 52) - 1;
169+
var bits = BitConverter.DoubleToInt64Bits(value);
170+
var exponent = (int)((bits >> 52) & 0x7ff);
171+
var significand = ((ulong)bits & fractionMask) | (exponent == 0 ? 0 : 1UL << 52);
172+
var adjustedExponent = exponent == 0 ? (significand == 0 ? 0 : -1022) : exponent - 1023;
173+
174+
if (precision is >= 0 and < 13)
175+
{
176+
var shift = 52 - precision * 4;
177+
var retained = significand >> shift;
178+
var discarded = significand & ((1UL << shift) - 1);
179+
var halfway = 1UL << (shift - 1);
180+
// Round to nearest, with ties to an even final hexadecimal digit.
181+
if (discarded > halfway || (discarded == halfway && (retained & 1) != 0))
182+
retained++;
183+
significand = retained << shift;
184+
}
185+
186+
var sign = bits < 0 ? "-" : "";
187+
var leadingDigit = (char)('0' + (significand >> 52));
188+
var fraction = significand & fractionMask;
189+
var digits = "";
190+
if (precision != 0 && (precision >= 0 || fraction != 0))
191+
{
192+
digits = fraction.ToString("x13", CultureInfo.InvariantCulture);
193+
digits =
194+
precision < 0 ? digits.TrimEnd('0')
195+
: precision < 13 ? digits[..precision]
196+
: digits.PadRight(precision, '0');
197+
}
198+
#if NET6_0_OR_GREATER
199+
if (digits.Length == 0 && !alternateForm)
200+
return string.Create(
201+
CultureInfo.InvariantCulture,
202+
$"{sign}0x{leadingDigit}p{adjustedExponent:+0;-0}"
203+
);
204+
return string.Create(
205+
CultureInfo.InvariantCulture,
206+
$"{sign}0x{leadingDigit}.{digits}p{adjustedExponent:+0;-0}"
207+
);
208+
#else
209+
if (digits.Length == 0 && !alternateForm)
210+
return string.Format(
211+
CultureInfo.InvariantCulture,
212+
"{0}0x{1}p{2:+0;-0}",
213+
sign,
214+
leadingDigit,
215+
adjustedExponent
216+
);
217+
return string.Format(
218+
CultureInfo.InvariantCulture,
219+
"{0}0x{1}.{2}p{3:+0;-0}",
220+
sign,
221+
leadingDigit,
222+
digits,
223+
adjustedExponent
224+
);
225+
#endif
226+
}
146227
}

0 commit comments

Comments
 (0)