想不通C中pow()函数的计算
Can't figure out the calculation of the pow() function in C
今天,我发现了一些我不知道的关于 C 中 math.h
的 pow()
函数的奇怪计算(在 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 中 math.h
的 pow()
函数的奇怪计算(在 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 正确评论的那样。