在下面的代码中,为什么 sum 和cast_sum 结果在 2^53 处存在分歧? pow(2, 53)
和 (uint64_6) pow(2, 53)
的结果看起来相同,但当我对它们求和时,得到不同的结果。总体目标是将 2^0 到 2^63 的结果相加。我只是不明白为什么使用 pow(2, i)
失败而 (uint64_t) pow(2, i)
有效。或者说为什么两者的结果从2^53开始就不同了。
#include <math.h>
#include <stdio.h>
int main() {
uint64_t sum = 0;
uint64_t cast_sum = 0;
for (int i = 0; i < 65; ++i) {
sum += pow(2,i);
cast_sum += (uint64_t) pow(2,i);
printf("i: %d, 2^%d = %lf, sum: %lu, cast_sum:%lu\n", i, i, pow(2, i), sum, cast_sum);
}
}
i: 52, 2^52 = 4503599627370496.000000, cast of 2^52: 4503599627370496, sum: 9007199254740991, cast_sum:9007199254740991
i: 53, 2^53 = 9007199254740992.000000, cast of 2^53: 9007199254740992, sum: 18014398509481984, cast_sum:18014398509481983
i: 54, 2^54 = 18014398509481984.000000, cast of 2^54: 18014398509481984, sum: 36028797018963968, cast_sum:36028797018963967```
最佳答案
Otherwise, if one operand is double, double complex, or double imaginary (since C99), the other operand is implicitly converted as follows: integer or real floating type to double
pow(2, i)
是一个双。 sum += pow(2,i);
从而转换sum
添加之前先将其变为 double ;它大致相当于sum = (uint64_t) (((double) sum) + pow(2, i));
.
2^53 并非巧合。 2^53 是 64 位 float (有 52 个尾数位加 1 个隐式位)可以准确表示的整数的限制。当sum
使用超过 53 个有效位并转换为 double ,一些较低有效位将在转换中丢失。
如果您在加法之前进行转换,那么一切都很好,因为您正在处理 64 位整数加法领域。请注意,使用指数尾数表示的 float 甚至可以精确地表示相当大的 2 的幂。所以pow(2, i)
可以完全适合您的小 i
。这可以并且将会被精确地转换为适当的 64 位无符号整数。
您应该使用位移1ULL << i
而不是pow(2, i)
尽管。取决于如何pow
实现后,它可能并不总是准确的。 Bitshifts 永远更可靠、更高效。
如果您想要所有位均为 1 的位模式,只需使用 ~0ULL
.
关于c - 为什么转换为 uint64_t 会改变此代码中的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76773989/