提问人:webHauser WM 提问时间:11/9/2023 最后编辑:Jonathan LefflerwebHauser WM 更新时间:11/10/2023 访问量:247
为什么我不能在 C/C++ 中用常量替换变量?
Why can't I replace a variable with a constant in C/C++?
问:
这是一个非常简单的 C/C++ 代码,让我感到惊讶,因为它没有给出相同的结果。
float f = -123.123f;
unsigned u1 = -123.123f;
unsigned u2 = f;
printf("%d %d\n", u1, u2); // 0 -123
printf("%u %u\n", u1, u2); // 0 4294967173
输出给出:
0 -123
0 4294967173
为什么结果不一样?
这令人担忧,因为具有相同含义的符号在语言中无法替代。
我在C++网站上用编译器测试了这个示例 programiz.com。
我预计结果是一样的。
感谢您的回答,让我们忘记印刷部分。据了解,它是未定义的,因此值可能因人而异。
答:
将小于或等于 −1 的浮点数转换为无符号整数类型的行为不是由 C 标准定义的。C 2018 6.3.1.4 1 说:
当实数浮点类型的有限值转换为 以外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。
_Bool
因此,对于 ,小数部分将被丢弃,剩下 −123。然后,整数部分 −123 不能用无符号类型表示,因此没有定义行为。-123.123f
在大多数使用无符号类型的运算中,数学结果是包装模 2w,其中 w 是无符号类型的宽度(以位为单位)。例如,或者将两者都包装数字并生成定义的结果(对于 16 位,该结果为 65,536 − 123 = 65,413,对于 32 位,为 4,294,967,296 − 123 − 123 = 4,294,967,173)。对于浮点转换,C 标准没有说发生这种包装。未定义行为。unsigned u = 0u - 123u;
unsigned u = -123;
unsigned int
unsigned int
在您的特定情况下可能发生的情况是,在编译时,编译器使用自己的代码生成一些结果,用于将浮点数转换为 ,并且此代码生成零,而对于从变量的转换,编译器生成处理器指令来执行转换,并且该处理器指令生成对应于 −123(二进制补码)或 4,294,967,173(无符号)的位。然后,当这些位被发送到要打印时,它会将这些位的解释打印为 ,产生 “-123” 的输出。(这是进一步未定义的行为,因为不应与 一起使用,并且 C 标准没有定义这种不匹配会发生什么。但是,将 的位解释为 an 是很常见的。unsigned
printf
%d
int
%d
unsigned int
unsigned int
int
printf("%d %d\n", u1, u2);
这具有未定义的行为,因为您使用的是 而不是 。%d
%u
unsigned u1 = -123.123f;
这具有未定义的行为,因为 的截断部分不能用 表示。来自 C23 草案:-123.123
unsigned
6.3.1.4 实浮点数和整数
- 当标准浮点型的有限值转换为除 之外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。65)
bool
65)将整数类型的值转换为无符号类型时执行的剩余操作不必是 当实际浮动类型的值转换为无符号类型时执行。因此,可移植实际浮点值的范围是
(−1, Utype_MAX + 1)。
- 当标准浮点型的有限值转换为除 之外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。65)
unsigned u2 = f;
This has the same issue as mentioned in the bullet just above.
A possible conversion with defined behavior:
#include <limits.h>
#include <stdio.h>
int main(void) {
float f = -123.123f;
unsigned u1 = (int)-123.123f; // the int value will be -123
unsigned u2 = (int)f; // again, the int value will be -123
printf("%u %u\n", u1, u2); // prints two large values
printf("%u\n", UINT_MAX - 122); // prints the same large value
}
The large values printed is because the variables will "wrap" around at zero:
, and is the same as .unsigned
0u - 1 == UINT_MAX
0u - 123
UINT_MAX - 122
评论
%d
unsigned