如何在c++中将64位整数乘以分数,同时最小化错误

How to multiply a 64 bit integer by a fraction in C++ while minimizing error?

本文关键字:错误 最小化 c++ 中将 整数 64位      更新时间:2023-10-16

给定64位(带符号)long long__int64,您如何将其乘以任意分数,同时最小化错误?

三个简单的草图:

int64_t numerator = ...;
int64_t denominator = ...;
int64_t x = ...;
// a, lossy double conversion for large values
double fraction = static_cast<double>(numerator) / static_cast<double>(denominator);
int64_t result = x * fraction;
// b, divide first, problem if denominator value near (or even larger) x
int64_t result = x / denominator;
result *= numerator;
// c, multiply first, problem if multiplication overflows
int64_t result = x * numerator;
result /= denominator;

如果x * n / d在数学上不能产生整数,我可以将结果截断到最接近的整数。

您可以使用以下命令:

const int64_t q = x / denominator;
const int64_t r = x - q * denominator;
// x = q * denominator + r;
const int64_t result = q * numerator + ((r * numerator) / denominator);

注意:用std::div族可以同时得到商和余。

注意:Sander De Dycker指出,当
r * numerator / denominator溢出
对于x / denominator溢出的特殊情况x = INT64_MIN, denominator = -1

或多或少是从这里复制的,这对我来说似乎是最有意义的:

int64_t muldiv64(const int64_t x, const int64_t n, const int64_t d)
{
    /* find the integer and remainder portions of x/d */
    const int64_t div = x / d;
    const int64_t mod = x % d;
    return div * n + (mod * n) / d;
}

改进提供的答案(这减少了b较大时的溢出):

int64_t muldiv64(const int64_t a, const int64_t b, const int64_t d)
{
    /* find the integer and remainder portions of x/d */
    const int64_t diva = a / d;
    const int64_t moda = a % d;
    const int64_t divb = b / d;
    const int64_t modb = b % d;
    return diva * b + moda * divb + moda * modb / d;
}

没有必要编写奇怪的代码来避免使用模数运算:编译器可以做替换,你可以有一个更可读的代码。

编辑:

任何更复杂的代码可能都不值得研究。如果需要更高的精度,最好是使用128位算法或使用任意精度的整数库(参见http://sourceforge.net/projects/cpp-bigint/)