static ParseError Base10ToBase2(ref float output, ulong mantissa10, int exponent10) { if (mantissa10 == 0) { output = 0.0f; return(ParseError.None); } if (exponent10 == 0) { output = mantissa10; return(ParseError.None); } var exponent2 = exponent10; var mantissa2 = mantissa10; while (exponent10 > 0) { while ((mantissa2 & 0xe000000000000000U) != 0) { mantissa2 >>= 1; ++exponent2; } mantissa2 *= 5; --exponent10; } while (exponent10 < 0) { while ((mantissa2 & 0x8000000000000000U) == 0) { mantissa2 <<= 1; --exponent2; } mantissa2 /= 5; ++exponent10; } // TODO: implement math.ldexpf (which presumably handles denormals (i don't)) UintFloatUnion ufu = new UintFloatUnion(); ufu.floatValue = mantissa2; var e = (int)((ufu.uintValue >> 23) & 0xFFU) - 127; e += exponent2; if (e > 128) { return(ParseError.Overflow); } if (e < -127) { return(ParseError.Underflow); } ufu.uintValue = (ufu.uintValue & ~(0xFFU << 23)) | ((uint)(e + 127) << 23); output = ufu.floatValue; return(ParseError.None); }
public static void Base2ToBase10(ref ulong mantissa10, ref int exponent10, float input) { UintFloatUnion ufu = new UintFloatUnion(); ufu.floatValue = input; if (ufu.uintValue == 0) { mantissa10 = 0; exponent10 = 0; return; } var mantissa2 = (ufu.uintValue & ((1 << 23) - 1)) | (1 << 23); var exponent2 = (int)(ufu.uintValue >> 23) - 127 - 23; // var tz = tzcnt((uint)mantissa2); // mantissa2 >>= tz; // exponent2 += tz; mantissa10 = mantissa2; exponent10 = exponent2; if (exponent2 > 0) { while (exponent2 > 0) { // denormalize mantissa10 as much as you can, to minimize loss when doing /5 below. while (mantissa10 <= UInt64.MaxValue / 10) { mantissa10 *= 10; --exponent10; } mantissa10 /= 5; --exponent2; } } if (exponent2 < 0) { while (exponent2 < 0) { // normalize mantissa10 just as much as you need, in order to make the *5 below not overflow. while (mantissa10 > UInt64.MaxValue / 5) { mantissa10 /= 10; ++exponent10; } mantissa10 *= 5; ++exponent2; } } // normalize mantissa10 while (mantissa10 > 9999999U || mantissa10 % 10 == 0) { mantissa10 = (mantissa10 + 5) / 10; ++exponent10; } }
public ParseError Parse(ref int offset, ref float output, char decimalSeparator) { if (Found(ref offset, 'n', 'a', 'n')) { UintFloatUnion ufu = new UintFloatUnion(); ufu.uintValue = 4290772992U; output = ufu.floatValue; return(ParseError.None); } int sign = 1; if (offset < Length) { if (buffer[offset] == '+') { ++offset; } else if (buffer[offset] == '-') { sign = -1; ++offset; } } ulong decimalMantissa = 0; int significantDigits = 0; int digitsAfterDot = 0; int mantissaDigits = 0; if (Found(ref offset, 'i', 'n', 'f', 'i', 'n', 'i', 't', 'y')) { output = (sign == 1) ? Single.PositiveInfinity : Single.NegativeInfinity; return(ParseError.None); } while (offset < Length && IsDigit(buffer[offset])) { ++mantissaDigits; if (significantDigits < 9) { var temp = decimalMantissa * 10 + (ulong)(buffer[offset] - '0'); if (temp > decimalMantissa) { ++significantDigits; } decimalMantissa = temp; } else { --digitsAfterDot; } ++offset; } if (offset < Length && buffer[offset] == decimalSeparator) { ++offset; while (offset < Length && IsDigit(buffer[offset])) { ++mantissaDigits; if (significantDigits < 9) { var temp = decimalMantissa * 10 + (ulong)(buffer[offset] - '0'); if (temp > decimalMantissa) { ++significantDigits; } decimalMantissa = temp; ++digitsAfterDot; } ++offset; } } if (mantissaDigits == 0) { return(ParseError.Syntax); } int decimalExponent = 0; int decimalExponentSign = 1; if (offset < Length && ((buffer[offset] | 32) == 'e')) { ++offset; if (offset < Length) { if (buffer[offset] == '+') { ++offset; } else if (buffer[offset] == '-') { decimalExponentSign = -1; ++offset; } } int exponentDigits = 0; while (offset < Length && IsDigit(buffer[offset])) { ++exponentDigits; decimalExponent = decimalExponent * 10 + (buffer[offset] - '0'); if (decimalExponent > 38) { if (decimalExponentSign == 1) { return(ParseError.Overflow); } else { return(ParseError.Underflow); } } ++offset; } if (exponentDigits == 0) { return(ParseError.Syntax); } } decimalExponent = decimalExponent * decimalExponentSign - digitsAfterDot; var error = Base10ToBase2(ref output, decimalMantissa, decimalExponent); if (error != ParseError.None) { return(error); } output *= sign; return(ParseError.None); }
public FormatError Format(float input, char decimalSeparator) { UintFloatUnion ufu = new UintFloatUnion(); ufu.floatValue = input; if (ufu.uintValue == 4290772992U) { return(Format('N', 'a', 'N')); } var sign = ufu.uintValue >> 31; ufu.uintValue &= ~(1 << 31); FormatError error; if (sign != 0 && ufu.uintValue != 0) // C# prints -0 as 0 { if ((error = Format('-')) != FormatError.None) { return(error); } } if (ufu.uintValue == 2139095040U) { return(Format('I', 'n', 'f', 'i', 'n', 'i', 't', 'y')); } ulong decimalMantissa = 0; int decimalExponent = 0; Base2ToBase10(ref decimalMantissa, ref decimalExponent, ufu.floatValue); var backwards = stackalloc char[9]; int decimalDigits = 0; do { if (decimalDigits >= 9) { return(FormatError.Overflow); } var decimalDigit = decimalMantissa % 10; backwards[8 - decimalDigits++] = (char)('0' + decimalDigit); decimalMantissa /= 10; } while (decimalMantissa > 0); char *ascii = backwards + 9 - decimalDigits; var leadingZeroes = -decimalExponent - decimalDigits + 1; if (leadingZeroes > 0) { if (leadingZeroes > 4) { return(FormatScientific(ascii, decimalDigits, decimalExponent, decimalSeparator)); } if ((error = Format('0', decimalSeparator)) != FormatError.None) { return(error); } --leadingZeroes; while (leadingZeroes > 0) { if ((error = Format('0')) != FormatError.None) { return(error); } --leadingZeroes; } for (var i = 0; i < decimalDigits; ++i) { if ((error = Format(ascii[i])) != FormatError.None) { return(error); } } return(FormatError.None); } var trailingZeroes = decimalExponent; if (trailingZeroes > 0) { if (trailingZeroes > 4) { return(FormatScientific(ascii, decimalDigits, decimalExponent, decimalSeparator)); } for (var i = 0; i < decimalDigits; ++i) { if ((error = Format(ascii[i])) != FormatError.None) { return(error); } } while (trailingZeroes > 0) { if ((error = Format('0')) != FormatError.None) { return(error); } --trailingZeroes; } return(FormatError.None); } var indexOfSeparator = decimalDigits + decimalExponent; for (var i = 0; i < decimalDigits; ++i) { if (i == indexOfSeparator) { if ((error = Format(decimalSeparator)) != FormatError.None) { return(error); } } if ((error = Format(ascii[i])) != FormatError.None) { return(error); } } return(FormatError.None); }