我需要在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;
}
这可能是“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
要了解为什么会发生这种情况,有一篇关于浮点数的好论文。 如果编译器上的 sizeof(long double) > 8,您可能会幸运地获得 long double 版本的日志。您还可以测试计算的“正确性”:
bool resultOk = upToBit(9007199254740992) == 238690780250636289.0;
一般来说,double有52位尾数,并且由于额外的隐藏位,double可以表示的最大可靠整数是2的53次方或
9007199254740992
。如果你得到的双精度值具有更高的整数值,那么简单的整数数学有时“停止工作”:
#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 进行内部计算。