public static bool TryParse(out Double value, string s, int ichMin, int ichLim, out int ichEnd) { bool neg = false; ulong num = 0; long exp = 0; ichEnd = ichMin; if (!TryParseCore(s, ref ichEnd, ichLim, ref neg, ref num, ref exp)) { return(TryParseSpecial(out value, s, ref ichEnd, ichLim)); } if (num == 0) { value = 0; goto LDone; } ulong mul; int e2; if (exp >= 0) { if (exp == 0) { // REVIEW: casting ulong to Double doesn't always get the answer correct. // Casting a non-negative long does work, though, so we jump through hoops to make // sure the top bit is clear and cast to long before casting to Double. if ((long)num >= 0) { value = (Double)(long)num; } else { value = (Double)(long)((num >> 1) | (num & 1)); value += value; } goto LDone; } if (exp <= 22 && num < (1UL << 53)) { // Can just use Double division, since both 10^|exp| and num can be exactly represented in Double. // REVIEW: there are potential other "easy" cases, like when num isn't less than 2^54, but // it ends with enough zeros that it can be made so by shifting. Contracts.Assert((long)(Double)(long)num == (long)num); value = (Double)(long)num * _mpe10Dbl[exp - 1]; goto LDone; } if (exp > _mpe10Man.Length) { value = Double.PositiveInfinity; goto LDone; } int index = (int)exp - 1; mul = _mpe10Man[index]; e2 = _mpe10e2[index]; } else { if (-exp <= 22 && num < (1UL << 53)) { // Can just use Double division, since both 10^|exp| and num can be exactly represented in Double. // REVIEW: there are potential other "easy" cases, like when num isn't less than 2^54, but // it ends with enough zeros that it can be made so by shifting. Contracts.Assert((long)(Double)(long)num == (long)num); value = (Double)(long)num / _mpe10Dbl[-exp - 1]; goto LDone; } if (-exp > _mpne10Man.Length) { value = 0; goto LDone; } int index = -(int)exp - 1; mul = _mpne10Man[index]; e2 = -_mpne10ne2[index]; } // Normalize the mantissa and initialize the base-2 (biased) exponent. // Ensure that the high bit is set. if ((num & 0xFFFFFFFF00000000UL) == 0) { num <<= 32; e2 -= 32; } if ((num & 0xFFFF000000000000UL) == 0) { num <<= 16; e2 -= 16; } if ((num & 0xFF00000000000000UL) == 0) { num <<= 8; e2 -= 8; } if ((num & 0xF000000000000000UL) == 0) { num <<= 4; e2 -= 4; } if ((num & 0xC000000000000000UL) == 0) { num <<= 2; e2 -= 2; } // Don't force the top bit to be non-zero, since we'll just have to clear it later.... // if ((num & 0x8000000000000000UL) == 0) { num <<= 1; e2 -= 1; } // The high bit of mul is 1, and at least one of the top two bits of num is non-zero. // Taking the high 64 bits of the 128 bit result should give us enough bits to get the // right answer most of the time. Note, that it's not guaranteed that we always get the // right answer. Guaranteeing that takes much more work.... See the paper by David Gay at // http://www.ampl.com/REFS/rounding.pdf. Contracts.Assert((num & TopTwoBits) != 0); Contracts.Assert((mul & TopBit) != 0); // Multiply mul into num, keeping the high ulong. // REVIEW: Can this be made faster? It would be nice to use the _umul128 intrinsic. ulong hi1 = mul >> 32; ulong lo1 = mul & 0xFFFFFFFFUL; ulong hi2 = num >> 32; ulong lo2 = num & 0xFFFFFFFFUL; num = hi1 * hi2; hi1 *= lo2; hi2 *= lo1; num += hi1 >> 32; num += hi2 >> 32; hi1 <<= 32; hi2 <<= 32; if ((hi1 += hi2) < hi2) { num++; } lo1 *= lo2; if ((lo1 += hi1) < hi1) { num++; } // Cast to long first since ulong => Double is broken. Contracts.Assert((num & TopThreeBits) != 0); if ((long)num >= 0) { // Capture a non-zero lo to avoid an artificial tie. if (lo1 != 0) { num |= 1; } value = (Double)(long)num; } else { if ((lo1 | (num & 1)) != 0) { num |= 2; } value = (Double)(long)(num >> 1); e2++; } // Adjust the base-2 exponent. e2 += 0x3FF; if (e2 >= 0x7FF) { Contracts.Assert(exp > 0); value = Double.PositiveInfinity; goto LDone; } if (e2 <= 0) { // Break the exponent adjustment into two operations. Contracts.Assert(exp < 0); mul = 1UL << 52; unsafe { value *= *(Double *)&mul; } e2 += 0x3FF - 1; Contracts.Assert(e2 > 0); } // Multiply by the exponent adjustment. Contracts.Assert(0 < e2 & e2 < 0x7FF); mul = (ulong)e2 << 52; unsafe { value *= *(Double *)&mul; } LDone: if (neg) { value = -value; } #if COMPARE_BCL string str = s.Substring(ichMin, ichEnd - ichMin); Double x; if (!Double.TryParse(str, out x)) { // Double.TryParse doesn't gracefully handle overflow to infinity. if (!Double.IsPositiveInfinity(value) && !Double.IsNegativeInfinity(value)) { if (!_failed) { _failed = true; Contracts.Assert(false, string.Format("Double.TryParse failed on: {0}", str)); } } } else if (FloatUtils.GetBits(x) != FloatUtils.GetBits(value)) { // Double.TryParse gets negative zero wrong! if (FloatUtils.GetBits(x) != 0 || FloatUtils.GetBits(value) != TopBit || !neg) { System.Diagnostics.Debug.WriteLine("*** FloatParser disagrees with Double.TryParse on: {0} ({1} vs {2})", str, FloatUtils.GetBits(x), FloatUtils.GetBits(value)); } } #endif return(true); }
public static bool TryParse(out Single value, string s, int ichMin, int ichLim, out int ichEnd) { bool neg = false; ulong num = 0; long exp = 0; ichEnd = ichMin; if (!TryParseCore(s, ref ichEnd, ichLim, ref neg, ref num, ref exp)) { return(TryParseSpecial(out value, s, ref ichEnd, ichLim)); } if (num == 0) { value = 0; goto LDone; } // The Single version simply looks up the power of 10 in a table (as a Double), casts num // to Double, multiples, then casts to Single. Double res; if (exp >= 0) { if (exp == 0) { res = 1; } else if (exp > _mpe10Dbl.Length) { value = Single.PositiveInfinity; goto LDone; } else { res = _mpe10Dbl[(int)exp - 1]; } } else { if (-exp > _mpne10Dbl.Length) { value = 0; goto LDone; } res = _mpne10Dbl[-(int)exp - 1]; } // REVIEW: casting ulong to Double doesn't always get the answer correct. // Casting a non-negative long does work, though, so we jump through hoops to make // sure the top bit is clear and cast to long before casting to Double. Double tmp; if ((long)num >= 0) { tmp = (Double)(long)num; } else { tmp = (Double)(long)((num >> 1) | (num & 1)); tmp += tmp; } res *= tmp; value = (Single)res; LDone: if (neg) { value = -value; } #if COMPARE_BCL if (!_failed) { string str = s.Substring(ichMin, ichEnd - ichMin); Single x; if (!Single.TryParse(str, out x)) { // Single.TryParse doesn't gracefully handle overflow to infinity. if (!Single.IsPositiveInfinity(value) && !Single.IsNegativeInfinity(value)) { _failed = true; Contracts.Assert(false, string.Format("Single.TryParse failed on: {0}", str)); } } else if (FloatUtils.GetBits(x) != FloatUtils.GetBits(value)) { // Double.TryParse gets negative zero wrong! if (FloatUtils.GetBits(x) != 0 || FloatUtils.GetBits(value) != 0x80000000U || !neg) { _failed = true; Contracts.Assert(false, string.Format("FloatParser disagrees with Single.TryParse on: {0} ({1} vs {2})", str, FloatUtils.GetBits(x), FloatUtils.GetBits(value))); } } } #endif return(true); }