Skip to content

Commit f1fbe01

Browse files
committed
fix ToString issue
1 parent e6f81e7 commit f1fbe01

File tree

5 files changed

+426
-52
lines changed

5 files changed

+426
-52
lines changed

Source/BaseTypes/BigFloat.cs

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ public static bool FromBigDec(
318318
return FromRational(numerator, denominator, significandSize, exponentSize, out result);
319319
}
320320

321+
#endregion
322+
323+
#region Validation and Parameter Checking
324+
321325
/// <summary>
322326
/// Validates that significand and exponent sizes meet minimum requirements (must be > 1)
323327
/// </summary>
@@ -360,6 +364,10 @@ private static void ValidateSizeCompatibility(BigFloat x, BigFloat y)
360364
/// </summary>
361365
private int GetActualExponent() => exponent == 0 ? 1 : (int)exponent;
362366

367+
#endregion
368+
369+
#region Arithmetic Helpers
370+
363371
private static (BigInteger significand, int exponent) PrepareOperand(BigFloat operand, BigInteger hiddenBit)
364372
{
365373
var sig = operand.significand;
@@ -782,6 +790,11 @@ private enum ArithmeticOperation
782790
/// </summary>
783791
/// <param name="floor">Floor (rounded towards negative infinity)</param>
784792
/// <param name="ceiling">Ceiling (rounded towards positive infinity)</param>
793+
794+
#endregion
795+
796+
#region Mathematical Operations
797+
785798
public void FloorCeiling(out BigInteger floor, out BigInteger ceiling)
786799
{
787800
// Handle special cases
@@ -913,10 +926,6 @@ [Pure] public override int GetHashCode() =>
913926
#region String Representation
914927

915928
[Pure]
916-
/// <summary>
917-
/// Converts the BigFloat to a decimal string representation.
918-
/// </summary>
919-
/// <returns>A decimal string representation of the value</returns>
920929
public string ToDecimalString()
921930
{
922931
// Handle special values per IEEE 754-2019 Section 5.12.1
@@ -956,41 +965,69 @@ public string ToDecimalString()
956965
public override string ToString()
957966
{
958967
Contract.Ensures(Contract.Result<string>() != null);
968+
969+
// Handle special values
959970
if (exponent == maxExponent) {
960971
return $"0{(significand == 0 ? $"{(signBit ? "-" : "+")}oo" : "NaN")}{SignificandSize}e{ExponentSize}";
961972
}
962973

963-
// Format as hex string
974+
// Handle zero
964975
if (IsZero) {
965976
return $"{(signBit ? "-" : "")}0x0.0e0f{SignificandSize}e{ExponentSize}";
966977
}
967978

968-
// Get mantissa with hidden bit and actual exponent
969-
var mantissa = exponent == 0 ? significand : significand | hiddenBit;
970-
var binaryExp = GetActualExponent() - (int)bias - (SignificandSize - 1);
979+
// Get mantissa and binary exponent
980+
var mantissa = IsSubnormal ? significand : (significand | hiddenBit);
981+
var binaryExp = IsSubnormal ? (1 - (int)bias) : ((int)exponent - (int)bias);
982+
binaryExp -= (SignificandSize - 1); // Adjust for mantissa bit position
971983

972-
// Calculate hex alignment
973-
var mantissaHex = mantissa.ToString("X");
974-
var totalShift = binaryExp + 4 * (mantissaHex.Length - 1);
975-
var hexExp = Math.DivRem(totalShift, 4, out var remainder);
984+
// Convert to hex representation
985+
// We want: mantissa * 2^binaryExp = hexMantissa * 16^hexExp
986+
// Since 16 = 2^4: mantissa * 2^binaryExp = hexMantissa * 2^(4*hexExp)
976987

977-
// Realign if needed
978-
if (remainder != 0) {
979-
mantissa <<= remainder > 0 ? remainder : 4 + remainder;
980-
hexExp -= remainder < 0 ? 1 : 0;
981-
mantissaHex = mantissa.ToString("X");
988+
// Start with the mantissa in hex
989+
var hexStr = mantissa.ToString("X");
990+
991+
// Calculate initial hex exponent (divide by 4, handle remainder with bit shifts)
992+
var hexExp = binaryExp / 4;
993+
var bitRemainder = binaryExp % 4;
994+
995+
// Adjust mantissa for the bit remainder
996+
if (bitRemainder != 0) {
997+
if (bitRemainder > 0) {
998+
mantissa <<= bitRemainder;
999+
} else {
1000+
// For negative remainder, shift left by (4 + remainder) and decrement hex exponent
1001+
mantissa <<= (4 + bitRemainder);
1002+
hexExp--;
1003+
}
1004+
hexStr = mantissa.ToString("X");
9821005
}
9831006

984-
// Format as x.y
985-
var hex = mantissaHex.TrimStart('0');
986-
if (string.IsNullOrEmpty(hex)) {
987-
hex = "0";
1007+
// Handle case where mantissa became zero (shouldn't happen for valid inputs)
1008+
if (hexStr == "0" || string.IsNullOrEmpty(hexStr)) {
1009+
return $"{(signBit ? "-" : "")}0x0.0e0f{SignificandSize}e{ExponentSize}";
9881010
}
9891011

990-
var frac = hex.Length > 1 ? hex[1..].TrimEnd('0') : "";
991-
var formatted = $"{hex[0]}.{(string.IsNullOrEmpty(frac) ? "0" : frac)}";
1012+
// Format as H.HHH (decimal point after first hex digit)
1013+
string formattedHex;
1014+
if (hexStr.Length == 1) {
1015+
formattedHex = $"{hexStr}.0";
1016+
} else {
1017+
var intPart = hexStr[..1];
1018+
var fracPart = hexStr[1..].TrimEnd('0');
1019+
if (fracPart.Length == 0) {
1020+
fracPart = "0";
1021+
}
1022+
1023+
formattedHex = $"{intPart}.{fracPart}";
9921024

993-
return $"{(signBit ? "-" : "")}0x{formatted}e{hexExp}f{SignificandSize}e{ExponentSize}";
1025+
// Adjust hex exponent for decimal point placement
1026+
// Moving decimal point left by (length-1) positions = multiplying by 16^(length-1)
1027+
hexExp += hexStr.Length - 1;
1028+
}
1029+
1030+
return $"{(signBit ? "-" : "")}0x{formattedHex}e{hexExp}f{SignificandSize}e{ExponentSize}";
9941031
}
9951032

9961033
/// <summary>
@@ -1003,6 +1040,11 @@ public override string ToString()
10031040
/// when false, follows IEEE 754 standard behavior</param>
10041041
/// <param name="result">The parsed BigFloat value if successful; default(BigFloat) otherwise</param>
10051042
/// <returns>True if the parse was successful; false otherwise</returns>
1043+
1044+
#endregion
1045+
1046+
#region String Parsing
1047+
10061048
private static bool TryParseHexFormat(string s, int sigSize, int expSize, bool strict, out BigFloat result)
10071049
{
10081050
result = default;
@@ -1084,7 +1126,10 @@ private static bool HandleUnderflow(bool signBit, BigInteger sig, int biasedExp,
10841126

10851127
// Calculate shift for subnormal representation
10861128
var msbPos = (int)sig.GetBitLength() - 1;
1087-
var shiftAmount = (1 - (int)bias) - actualExp + msbPos;
1129+
// Bit position 0 in subnormal represents 2^(1-bias-(sigSize-1))
1130+
// We need to shift from msbPos to the correct position for actualExp
1131+
var subnormalBase = 1 - (int)bias - (sigSize - 1);
1132+
var shiftAmount = msbPos - (actualExp - subnormalBase);
10881133

10891134
// Apply shift and check result
10901135
var subnormalSig = shiftAmount > 0 ? sig >> shiftAmount : sig << (-shiftAmount);

0 commit comments

Comments
 (0)