想不通C中pow()函数的计算

标签 c floating-point pow math.h

今天,我发现了一些我不知道的关于 C 中 pow()math.h 函数的奇怪计算(在 SO 中也没有发现任何与此相关的问题)。

我正在试验的代码是:

#include <stdio.h>
#include <math.h>

int main(){
    //1st case
    long long result = pow(2, 63);
    printf("1st case: %lld\n", result);

    //2nd case
    result = pow(2, 64);
    printf("2nd case: %lld\n", result);

    //3rd case
    printf("3rd case: %lld\n", (long long) (pow(2, 64)/12000));

    //4th case
    result = pow(2, 64);
    result = result/12000;
    printf("4th case: %lld\n", result);

    //5th case
    result = pow(2, 64) / 12000;
    printf("5th case: %lld\n", result); 

    return 0;
}

输出是:

1st case: 9223372036854775807
2nd case: 9223372036854775807
3rd case: 1537228672809129
4th case: 768614336404564
5th case: 1537228672809129

我有几个问题不是很清楚。我正在使用 CodeBlocks 16.01 IDE 和 GNU GCC 编译器。

1. 在第一种情况下,输出是 2^63 = 9223372036854775807 。为什么会这样?最后一位数字在这里是奇数。它应该是 9223372036854775808

2. 在第二种情况下,2^64 = 9223372036854775807 与之前计算的 2^63 相同(实际上对于任何大于 63 的指数值都是相同的)。是的,我知道 long long int 数据类型发生溢出。但是为什么赋值错误呢?

3. 第3种情况和第5种情况(其实是一样的),答案是正确的。但是在第 4 种情况下,答案是错误的。根据算术运算,第4种情况的数学运算应该与其余两种相同,但实际上并非如此。那么区别在哪里呢?

最佳答案

这些警告(像 gcc prog.c -Wall -Wextra 一样编译)对你来说还不够吗?

prog.c: In function 'main':
prog.c:6:24: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)9.2233720368547758e+18' to '9223372036854775807' [-Woverflow]
     long long result = pow(2, 63);
                        ^~~
prog.c:10:14: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)1.8446744073709552e+19' to '9223372036854775807' [-Woverflow]
     result = pow(2, 64);
              ^~~
prog.c:17:14: warning: overflow in conversion from 'double' to 'long long int' changes value from '(double)1.8446744073709552e+19' to '9223372036854775807' [-Woverflow]
     result = pow(2, 64);
              ^~~

我的意思是发生溢出,然后发生舍入,这给你这个输出,因为实际值根本不适合该数据类型。如果它是一个long double,事情就会不同。 (不要忘记用 %Lf 打印。)

阅读更多 What causes floating point rounding errors?


PS:在情况 3 中,您在转换为 long long 之前进行除法,因此它使用浮点值,这是正确的,正如 Barmar 正确评论的那样。

关于想不通C中pow()函数的计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48457915/

相关文章:

c++ - 为什么在浮点文字的末尾添加 0 会改变它的舍入方式(可能是 GCC 错误)?

c - 为什么我的计算给出 'inf' 而不是一个数字?

javascript - 模 % 大数 - 无穷大错误 - Javascript

c++ - 来自 math.h 库的 pow() - 如何使用函数应用

C 和 Lua - 将 native 对象实例传递给 native Lua 函数

c - scanf函数测试

c - 如何知道是否使用了 ASCII?

C#,从 USART 接收字符缓冲区中提取 double

c# - Math.Pow 与乘法运算符(性能)

binary - 是否可以将最高有效的十进制数字精度转换为二进制并返回十进制而又不丢失有效值6或7.225?