void SubtractTimes(Bignum other, uint factor) { #if DEBUG var a = new Bignum(); var b = new Bignum(); a.AssignBignum(this); b.AssignBignum(other); b.MultiplyByUInt32(factor); a.SubtractBignum(b); #endif Debug.Assert(exponent_ <= other.exponent_); if (factor < 3) { for (int i = 0; i < factor; ++i) { SubtractBignum(other); } return; } uint borrow = 0; int exponent_diff = other.exponent_ - exponent_; for (int i = 0; i < other.used_digits_; ++i) { ulong product = factor * other.bigits_[i]; ulong remove = borrow + product; uint difference = bigits_[i + exponent_diff] - (uint)(remove & kBigitMask); bigits_[i + exponent_diff] = difference & kBigitMask; borrow = (uint)((difference >> (kChunkSize - 1)) + (remove >> kBigitSize)); } for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { if (borrow == 0) { return; } uint difference = bigits_[i] - borrow; bigits_[i] = difference & kBigitMask; borrow = difference >> (kChunkSize - 1); } Clamp(); #if DEBUG Debug.Assert(Equal(a, this)); #endif }
// This routine multiplies numerator/denominator so that its values lies in the // range 1-10. That is after a call to this function we have: // 1 <= (numerator + delta_plus) /denominator < 10. // Let numerator the input before modification and numerator' the argument // after modification, then the output-parameter decimal_point is such that // numerator / denominator * 10^estimated_power == // numerator' / denominator' * 10^(decimal_point - 1) // In some cases estimated_power was too low, and this is already the case. We // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == // estimated_power) but do not touch the numerator or denominator. // Otherwise the routine multiplies the numerator and the deltas by 10. private static void FixupMultiply10( int estimated_power, bool is_even, out int decimal_point, Bignum numerator, Bignum denominator, Bignum delta_minus, Bignum delta_plus) { bool in_range; if (is_even) { in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) >= 0; } else { in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) > 0; } if (in_range) { // Since numerator + delta_plus >= denominator we already have // 1 <= numerator/denominator < 10. Simply update the estimated_power. decimal_point = estimated_power + 1; } else { decimal_point = estimated_power; numerator.Times10(); if (Bignum.Equal(delta_minus, delta_plus)) { delta_minus.Times10(); delta_plus.AssignBignum(delta_minus); } else { delta_minus.Times10(); delta_plus.Times10(); } } }
// See comments for InitialScaledStartValues private static void InitialScaledStartValuesNegativeExponentNegativePower( double v, int estimated_power, bool need_boundary_deltas, Bignum numerator, Bignum denominator, Bignum delta_minus, Bignum delta_plus) { const ulong kMinimalNormalizedExponent = 0x0010000000000000; var bits = (ulong)BitConverter.DoubleToInt64Bits(v); ulong significand = DoubleHelper.Significand(bits); int exponent = DoubleHelper.Exponent(bits); // Instead of multiplying the denominator with 10^estimated_power we // multiply all values (numerator and deltas) by 10^-estimated_power. // Use numerator as temporary container for power_ten. Bignum power_ten = numerator; power_ten.AssignPowerUInt16(10, -estimated_power); if (need_boundary_deltas) { // Since power_ten == numerator we must make a copy of 10^estimated_power // before we complete the computation of the numerator. // delta_plus = delta_minus = 10^estimated_power delta_plus.AssignBignum(power_ten); delta_minus.AssignBignum(power_ten); } // numerator = significand * 2 * 10^-estimated_power // since v = significand * 2^exponent this is equivalent to // numerator = v * 10^-estimated_power * 2 * 2^-exponent. // Remember: numerator has been abused as power_ten. So no need to assign it // to itself. numerator.MultiplyByUInt64(significand); // denominator = 2 * 2^-exponent with exponent < 0. denominator.AssignUInt16(1); denominator.ShiftLeft(-exponent); if (need_boundary_deltas) { // Introduce a common denominator so that the deltas to the boundaries are // integers. numerator.ShiftLeft(1); denominator.ShiftLeft(1); // With this shift the boundaries have their correct value, since // delta_plus = 10^-estimated_power, and // delta_minus = 10^-estimated_power. // These assignments have been done earlier. // The special case where the lower boundary is twice as close. // This time we have to look out for the exception too. ulong v_bits = bits; if ((v_bits & DoubleHelper.KSignificandMask) == 0 && // The only exception where a significand == 0 has its boundaries at // "normal" distances: (v_bits & DoubleHelper.KExponentMask) != kMinimalNormalizedExponent) { numerator.ShiftLeft(1); // *2 denominator.ShiftLeft(1); // *2 delta_plus.ShiftLeft(1); // *2 } } }