// this = this * other. void Multiply(DiyFp other) { // Simply "emulates" a 128 bit multiplication. // However: the resulting number only contains 64 bits. The least // significant 64 bits are only used for rounding the most significant 64 // bits. const ulong kM32 = 0xFFFFFFFFU; ulong a = F >> 32; ulong b = F & kM32; ulong c = other.F >> 32; ulong d = other.F & kM32; ulong ac = a * c; ulong bc = b * c; ulong ad = a * d; ulong bd = b * d; ulong tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); // By adding 1U << 31 to tmp we round the final result. // Halfway cases will be round up. tmp += 1U << 31; ulong result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); E += other.E + 64; F = result_f; }
// returns a * b; public static DiyFp operator *(DiyFp a, DiyFp b) { DiyFp result = a; result.Multiply(b); return(result); }
// Returns a - b. // The exponents of both numbers must be the same and this must be bigger // than other. The result will not be normalized. public static DiyFp operator -(DiyFp a, DiyFp b) { DiyFp result = a; result.Subtract(b); return(result); }
public static DiyFp Normalize(DiyFp a) { DiyFp result = a; result.Normalize(); return(result); }
public static bool ToString( double v, bool isSinglePrecision, Span <byte> buffer, out int length, out int decimal_exponent) { DiyFp w = new DoubleView(v).AsNormalizedDiyFp(); DiyFp boundary_minus, boundary_plus; if (!isSinglePrecision) { new DoubleView(v).NormalizedBoundaries(out boundary_minus, out boundary_plus); } else { new SingleView((float)v).NormalizedBoundaries(out boundary_minus, out boundary_plus); } Debug.Assert(boundary_plus.E == w.E); int ten_mk_minimal_binary_exponent = kMinimalTargetExponent - (w.E + DiyFp.SignificandSize); int ten_mk_maximal_binary_exponent = kMaximalTargetExponent - (w.E + DiyFp.SignificandSize); PowersOfTenCache.GetCachedPowerForBinaryExponentRange( ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, out var ten_mk, out var mk); Debug.Assert((kMinimalTargetExponent <= w.E + ten_mk.E + DiyFp.SignificandSize) && (kMaximalTargetExponent >= w.E + ten_mk.E + DiyFp.SignificandSize)); DiyFp scaled_w = (w * ten_mk); Debug.Assert(scaled_w.E == boundary_plus.E + ten_mk.E + DiyFp.SignificandSize); DiyFp scaled_boundary_minus = (boundary_minus * ten_mk); DiyFp scaled_boundary_plus = (boundary_plus * ten_mk); bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, buffer, out length, out var kappa); decimal_exponent = -mk + kappa; return(result); }
public void NormalizedBoundaries(out DiyFp out_m_minus, out DiyFp out_m_plus) { Debug.Assert(Value > 0.0); DiyFp v = AsDiyFp(); DiyFp m_plus = DiyFp.Normalize(new DiyFp((v.F << 1) + 1, v.E - 1)); DiyFp m_minus; if (LowerBoundaryIsCloser()) { m_minus = new DiyFp((v.F << 2) - 1, v.E - 2); } else { m_minus = new DiyFp((v.F << 1) - 1, v.E - 1); } m_minus.F = m_minus.F << (m_minus.E - m_plus.E); m_minus.E = m_plus.E; out_m_plus = m_plus; out_m_minus = m_minus; }
// this = this - other. // The exponents of both numbers must be the same and the significand of this // must be bigger than the significand of other. // The result will not be normalized. void Subtract(DiyFp other) { Debug.Assert(E == other.E); Debug.Assert(F >= other.F); F -= other.F; }
private static bool DigitGen( DiyFp low, DiyFp w, DiyFp high, Span <byte> buffer, out int length, out int kappa) { Debug.Assert(low.E == w.E && w.E == high.E); Debug.Assert(low.F + 1 <= high.F - 1); Debug.Assert(kMinimalTargetExponent <= w.E && w.E <= kMaximalTargetExponent); ulong unit = 1; DiyFp too_low = new DiyFp(low.F - unit, low.E); DiyFp too_high = new DiyFp(high.F + unit, high.E); DiyFp unsafe_interval = too_high - too_low; DiyFp one = new DiyFp(1UL << -w.E, w.E); uint integrals = unchecked ((uint)(too_high.F >> -one.E)); ulong fractionals = too_high.F & (one.F - 1); BiggestPowerTen(integrals, DiyFp.SignificandSize - (-one.E), out var divisor, out var divisor_exponent_plus_one); kappa = divisor_exponent_plus_one; length = 0; while (kappa > 0) { int digit = unchecked ((int)(integrals / divisor)); Debug.Assert(digit <= 9); buffer[length] = (byte)('0' + digit); length++; integrals %= divisor; kappa--; ulong rest = ((ulong)(integrals) << -one.E) + fractionals; if (rest < unsafe_interval.F) { return(RoundWeed(buffer, length, (too_high - w).F, unsafe_interval.F, rest, (ulong)(divisor) << -one.E, unit)); } divisor /= 10; } Debug.Assert(one.E >= -60); Debug.Assert(fractionals < one.F); Debug.Assert(0xFFFFFFFF_FFFFFFFFUL / 10 >= one.F); for (; ;) { fractionals *= 10; unit *= 10; unsafe_interval.F = (unsafe_interval.F * 10); int digit = unchecked ((int)(fractionals >> -one.E)); Debug.Assert(digit <= 9); buffer[length] = (byte)('0' + digit); (length)++; fractionals &= one.F - 1; // Modulo by one. (kappa)--; if (fractionals < unsafe_interval.F) { return(RoundWeed(buffer, length, (too_high - w).F * unit, unsafe_interval.F, fractionals, one.F, unit)); } } }