c - 为什么转换为 uint64_t 会改变此代码中的结果?

标签 c

在下面的代码中,为什么 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```

最佳答案

参见C's implicit conversions :

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/

相关文章:

c - 找不到 Eclipse mac osx : Launch failed, 二进制文件

c - 在c中传递数组元素指针

c - Bash 脚本中的流程控制和返回值

c - 我的链表程序出了什么问题? (分段故障)

c - 如何将字符串转换为枚举数?

c - 如何发送进程间消息?

c++ - 用 C++ 或 C 绘制 igraph 数据?

c - 如何使用 strtok() 从文本文件中读取并创建结构?

c - 如何删除 char 数组转换为无符号 char 数组的警告

c - 即使不包含在 C 中,函数也是可见的