unsigned long long int参数的日志

时间:2017-05-01 17:37:38

标签: c++ cmath

我需要在c ++代码中进行以下计算:

(((n*log(n)) / log(4)) + 1)

其中n的类型为'unsigned long long int'(并且是2的幂,因此结果应该是整数)。

对于非常大的数字,我会收到一些错误,例如n = 9007199254740992结果应为238690780250636289,但是当我运行代码时,我会得到238690780250636288

这可能是'log'函数没有'unsigned long long int'参数的实现的结果吗?如果没有实现新的日志功能,有没有办法绕过它呢?

unsigned long long int upToBit(unsigned long long int n) {
    unsigned long long int check = (((n*log(n)) / log(4)) + 1);
    return check;
}

1 个答案:

答案 0 :(得分:7)

  

这可能是'log'函数没有'unsigned long long int'参数的实现的结果吗?

是和否。

你使用std :: log返回double。由于扩展范围,双倍不能代表238690780250636289。如果您只是将该数字转换为long long,您将得到完全相同的错误:

int main()
{
    volatile double dd = 238690780250636289.0;
    printf("%lld\n", (long long)dd);
}

输出:

238690780250636288

要了解发生这种情况的原因,有a good paper about floating point numbers。 如果sizeof(long double)是>你可能会对长双倍版本的日志有好运。 8在您的编译器上。您还可以测试计算的“正确性”:

bool resultOk = upToBit(9007199254740992) == 238690780250636289.0;

通常,double具有52位尾数,并且由于额外隐藏位最大可靠整数,双倍可以表示为2次幂53或9007199254740992。如果你得到的double有更高的整数值,那么简单整数数学有时会"stops working"

#include <stdio.h>

int main()
{
    long long l = 9007199254740992;
    double d = (double)l;
    d += 1;
    printf("9007199254740992 + 1 = %lld\n", (long long)d);
}

输出:

9007199254740992 + 1 = 9007199254740992

为了获得更好的精度,您可以使用一些旨在实现此目的的多个精度算术库。例如,GCC使用GMP / MPFR进行内部计算。