/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_RoundedMulDiv_h #define mozilla_RoundedMulDiv_h #include "mozilla/Assertions.h" #include "mozilla/CheckedArithmetic.h" #include "mozilla/MathAlgorithms.h" #include namespace mozilla { // Returns aValue * aMultiplier / aDivisor rounded to the nearest integer, // computed entirely in 64-bit integer arithmetic to avoid floating-point // representation error. Saturates to INT64_MAX / INT64_MIN (by sign) if the // rounded result would not fit int64_t. inline int64_t RoundedMulDiv(int64_t aValue, uint64_t aMultiplier, uint64_t aDivisor) { MOZ_ASSERT(aDivisor != 0, "Division by zero"); const bool negative = aValue < 0; uint64_t magnitude = negative ? ~static_cast(aValue) + 1u : static_cast(aValue); // Reduce the exact rational value first. This preserves the rounded result // and gives the arithmetic below the smallest products to work with. uint64_t g = GCD(magnitude, aDivisor); magnitude /= g; aDivisor /= g; g = GCD(aMultiplier, aDivisor); aMultiplier /= g; aDivisor /= g; // Let magnitude = q * aDivisor + r, with 0 <= r < aDivisor. Then, for // nonnegative inputs: // round(magnitude * aMultiplier / aDivisor) // = q * aMultiplier + (r * aMultiplier + aDivisor / 2) / aDivisor [1] // // [1] Since q is already an integer, rounding only affects the fractional // part: // round(magnitude * aMultiplier / aDivisor) // = q * aMultiplier + round(r * aMultiplier / aDivisor) // // For any integer N >= 0, round(N / D) equals ⌊(N + D/2) / D⌋: // writing N = k * D + s with 0 <= s < D gives // ⌊(N + D/2) / D⌋ = k + ⌊(s + D/2) / D⌋, and // if 0 <= s < D/2, then 1/2 <= (s + D/2) / D < 1, so ⌊(s + D/2) / D⌋ = 0; // if D/2 <= s < D, then 1 <= (s + D/2) / D < 3/2, so ⌊(s + D/2) / D⌋ = 1. // Applying this with N = r * aMultiplier and D = aDivisor: // round(magnitude * aMultiplier / aDivisor) // = q * aMultiplier + ⌊(r * aMultiplier + aDivisor / 2) / aDivisor⌋. const uint64_t q = magnitude / aDivisor; const uint64_t r = magnitude % aDivisor; // Limitation: r * aMultiplier is formed before the division by aDivisor, so // this intermediate can overflow uint64_t even when the final result fits // int64_t. Its contribution to the result, // ⌊(r * aMultiplier + aDivisor / 2) / aDivisor⌋, // is at most aMultiplier, but the un-divided r * aMultiplier is up to // aMultiplier * (aDivisor - 1), since r <= aDivisor - 1. So a large // aMultiplier and aDivisor can overflow the product even while the result // stays small. On the other hand, q * aMultiplier cannot overflow this way, // because q = magnitude / aDivisor already carries the division, // q * aMultiplier is at most the final result, so it overflows only when that // result itself would exceed uint64_t. uint64_t whole; uint64_t fractional; if (!SafeMul(q, aMultiplier, &whole) || !SafeMul(r, aMultiplier, &fractional) || !SafeAdd(fractional, aDivisor / 2, &fractional)) { return negative ? INT64_MIN : INT64_MAX; } uint64_t quotient; if (!SafeAdd(whole, fractional / aDivisor, "ient)) { return negative ? INT64_MIN : INT64_MAX; } if (quotient > static_cast(INT64_MAX)) { return negative ? INT64_MIN : INT64_MAX; } return negative ? -static_cast(quotient) : static_cast(quotient); } } // namespace mozilla #endif /* mozilla_RoundedMulDiv_h */