对于此函数,它可以计算最多n = 64
的伍尔特数
Woodall的算法是W n = n⋅2 n -1
for (int n = 1; n <= 64; ++n)
{
a[n - 1] = (n * (exp2(n))) - 1;
}
但是在n
大于47之后,结果是错误的,因为似乎忘记了- 1
的{{1}}的结果。
如果我通过
n * (exp2(n))
的值,这就是输出
cout
...之前是正确的
std::cout << i << ":\t" << std::setprecision(32) << a[i - 1] << std::endl;
...之后不正确
对于n
45: 1583296743997439
46: 3236962232172543
47: 6614661952700415
48: 13510798882111488
49: 27584547717644288
50: 56294995342131200
是一个无符号的long int
如果我将a[]
操作分离为自己的for循环,则该函数会产生正确的结果:
- 1
答案 0 :(得分:5)
exp2(n)
返回一个double
。
在IEEE754(浮点类型的非常常见的规范)中,它仅给您精确的整数,直到52的2的次幂。此后,您就可以得出近似值。
由于隐式类型转换,整个表达式n * (exp2(n))) - 1
是double
,因此您会注意到第52 Woodall号之前的问题。 通过计算问题,是导致问题的-1。恰好另一个项是2的幂的适当倍数,因此可以将其表示为没有精度损失的双精度!! 这是第二个代码段起作用但第一个代码段不起作用的原因。>
在具有64位int
的系统上,您将以2的63次幂达到整数限制(以及未定义的行为)。
您最好的选择是纯粹用unsigned
算术生成Woodall数(请注意<<
与2的幂之间的关系),甚至可能对连续的Woodall数使用递归关系。
答案 1 :(得分:0)
double
具有精度限制。不过,它确实使用二进制基数来工作,这意味着大多数以二进制零位序列结尾的数字都可以精确表示,exp2(int)
的倍数就是这种情况。
50 * exp2(50)
(例如56294995342131200
)是十六进制的C8000000000000
。即使数字位数超过double
的精度限制,也可以准确表示。但是,如果我尝试从该数字中加减1
,情况就不再如此。
double
既不能表示56294995342131199
也不能表示56294995342131201
,所以当您尝试这样做时,它只会四舍五入到56294995342131200
。
这就是您的- 1
位失败的原因,当您尝试执行此操作时,它仍作为double
运行。在执行此减法之前,您必须将表达式的其余部分强制转换为int64_t
。
但是另一种解决方案是根本不使用exp2()
。由于我们正在使用整数,因此您可以简单地使用按位运算来执行相同的任务。 (1 << n)
将获得与exp2()
相同的结果,只是它现在是整数格式,并且由于您只是将其乘以n
,因此实际上可以执行(n << n)
。
当然,这仍然会打破常规。 int64_t
最多只能容纳2 63 -1和uint64_t
2 64 -1的数字,当迭代器达到n = 57
。