如果指数是变量,为什么从 pow() 输出的超大值不以相同的方式强制转换?
Why aren't oversized values outputted from pow() coerced the same way if the exponent is a variable?
我在做一些按位练习时遇到了这个不寻常的错误。当 pow()
的输出被强制为 unsigned int
时,pow()
的结果用 变量 调用,因为指数变为零,而结果当指数是 文字整数时 被强制 通常 为 0xFFFFFFFF (2^32 - 1)。只有当值 过大 时才会发生这种情况,在本例中为 2^32。用作指数参数的变量类型似乎不会影响此结果。我还尝试将对 pow()
的两次调用的输出存储为双精度值,然后在引用变量时应用强制转换;差距依然存在。
#import <math.h>
int main (void) {
int thirtytwo = 32; // double, unsigned, etc... all yielded the same result
printf("Raw Doubles Equal: %s\n", pow(2, 32) == pow(2, thirtytwo) ? "true" : "false"); // -> true
printf("Coerced to Unsigned Equal: %s\n", (unsigned) pow(2, 32) == (unsigned) pow(2, thirtytwo) ? "true": "false"); // -> false
return 0;
}
出于好奇,我 运行 通过 clang/llvm 使用相同的代码,得到了不同的结果:无论指数是否为变量,将结果强制转换为 unsigned int 会产生零 (正如预期的那样)。
编辑:最大 32 位无符号整数为 2^32 - 1
,因此强制输出实际上 不正确 。我的错误是超出了整数大小限制。为什么 gcc 基本上四舍五入到最大整数值是一个有趣的好奇心,但不是特别重要。
编译器将使用常量折叠将 pow(2, 32)
替换为常量结果; pow(2, thirtytwo)
将在 运行 时间内计算。 C11 实际上允许编译时计算 比相应的 运行 时间计算 (C11 6.6p5) 更 精确:
If a floating expression is evaluated in the translation environment, the arithmetic range and precision shall be at least as great as if the expression were being evaluated in the execution environment.
例如,众所周知,GCC 就是这样做的。因此 C 标准并不真正要求第一个打印 true
(and in practice this does occur in some implementations)。
至于第二个为什么打印false
:是因为pow(2 ^ 32)
不能用32位的unsigned int表示。 C11 6.3.1.4:
When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
因此第二个打印 false
是由于未定义的行为。与将整数转换缩小为无符号整数不同,对于从浮点数到偶数无符号整数的转换,溢出是明确未定义的。
值得注意的是,我的 GCC 6.2.0 无法警告 (unsigned int)pow(2, 32)
的编译时未定义行为。 (我试过 -lm -Wall -Werror -pedantic -ubsan -Wfloat-conversion -Wconversion -Wextra -std=c11
但没有任何输出)。
我在做一些按位练习时遇到了这个不寻常的错误。当 pow()
的输出被强制为 unsigned int
时,pow()
的结果用 变量 调用,因为指数变为零,而结果当指数是 文字整数时 被强制 通常 为 0xFFFFFFFF (2^32 - 1)。只有当值 过大 时才会发生这种情况,在本例中为 2^32。用作指数参数的变量类型似乎不会影响此结果。我还尝试将对 pow()
的两次调用的输出存储为双精度值,然后在引用变量时应用强制转换;差距依然存在。
#import <math.h>
int main (void) {
int thirtytwo = 32; // double, unsigned, etc... all yielded the same result
printf("Raw Doubles Equal: %s\n", pow(2, 32) == pow(2, thirtytwo) ? "true" : "false"); // -> true
printf("Coerced to Unsigned Equal: %s\n", (unsigned) pow(2, 32) == (unsigned) pow(2, thirtytwo) ? "true": "false"); // -> false
return 0;
}
出于好奇,我 运行 通过 clang/llvm 使用相同的代码,得到了不同的结果:无论指数是否为变量,将结果强制转换为 unsigned int 会产生零 (正如预期的那样)。
编辑:最大 32 位无符号整数为 2^32 - 1
,因此强制输出实际上 不正确 。我的错误是超出了整数大小限制。为什么 gcc 基本上四舍五入到最大整数值是一个有趣的好奇心,但不是特别重要。
编译器将使用常量折叠将 pow(2, 32)
替换为常量结果; pow(2, thirtytwo)
将在 运行 时间内计算。 C11 实际上允许编译时计算 比相应的 运行 时间计算 (C11 6.6p5) 更 精确:
If a floating expression is evaluated in the translation environment, the arithmetic range and precision shall be at least as great as if the expression were being evaluated in the execution environment.
例如,众所周知,GCC 就是这样做的。因此 C 标准并不真正要求第一个打印 true
(and in practice this does occur in some implementations)。
至于第二个为什么打印false
:是因为pow(2 ^ 32)
不能用32位的unsigned int表示。 C11 6.3.1.4:
When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
因此第二个打印 false
是由于未定义的行为。与将整数转换缩小为无符号整数不同,对于从浮点数到偶数无符号整数的转换,溢出是明确未定义的。
值得注意的是,我的 GCC 6.2.0 无法警告 (unsigned int)pow(2, 32)
的编译时未定义行为。 (我试过 -lm -Wall -Werror -pedantic -ubsan -Wfloat-conversion -Wconversion -Wextra -std=c11
但没有任何输出)。