【问题标题】:log of unsigned long long int argumentunsigned long long int 参数的日志
【发布时间】:2017-05-01 17:37:37
【问题描述】:

我需要在 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;
}

【问题讨论】:

  • JFYI,C++11 允许简化计算:(((n*log(n)) / log(4)) + 1) = (((n*log(n)) / (2*log(2))) + 1) = n/2 * std::log2(n) + 1
  • 哇,这么多括号!难怪我在需要的时候找不到。

标签: c++ cmath


【解决方案1】:

这可能是“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,您可能会遇到 long double 版本的 log。您还可以测试计算的“正确性”:

bool resultOk = upToBit(9007199254740992) == 238690780250636289.0;

通常,double 具有 52 位尾数,并且由于额外的隐藏位,double 可以表示的最大可靠整数是 53 的 2 次方或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 进行内部计算。

【讨论】:

  • 那么有解决办法吗?
  • @sniper 你可以使用像gmp 这样的库,它允许你对任意大的数字进行算术运算(超出内置类型的范围)。跨度>
猜你喜欢
  • 2016-07-29
  • 1970-01-01
  • 2012-01-27
  • 2016-12-20
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
相关资源
最近更新 更多