为什么我不能在 C/C++ 中用常量替换变量?

Why can't I replace a variable with a constant in C/C++?

提问人:webHauser WM 提问时间:11/9/2023 最后编辑:Jonathan LefflerwebHauser WM 更新时间:11/10/2023 访问量:247

问:

这是一个非常简单的 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。

我预计结果是一样的。

感谢您的回答,让我们忘记印刷部分。据了解,它是未定义的,因此值可能因人而异。

C++ C 数学

评论

6赞 PaulMcKenzie 11/9/2023
这令人担忧......-- 你是说你真的要写这样的代码吗?
8赞 Ted Lyngmo 11/9/2023
注意:该程序具有未定义的行为,因为您用于变量,我认为使用正确的转换说明符时的结果会让您更加惊讶。%dunsigned
3赞 Thomas Matthews 11/9/2023
仅供参考,您正在从浮点数转换为整数。这涉及截断。
10赞 Shawn 11/9/2023
C 和 C++ 是不同的语言。选择一个。
3赞 Barmar 11/9/2023
@ThomasMatthews 这并不能解释为什么转换会有所不同,具体取决于源是浮点变量还是浮点数。

答:

12赞 Eric Postpischil 11/9/2023 #1

将小于或等于 −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 intunsigned int

在您的特定情况下可能发生的情况是,在编译时,编译器使用自己的代码生成一些结果,用于将浮点数转换为 ,并且此代码生成零,而对于从变量的转换,编译器生成处理器指令来执行转换,并且该处理器指令生成对应于 −123(二进制补码)或 4,294,967,173(无符号)的位。然后,当这些位被发送到要打印时,它会将这些位的解释打印为 ,产生 “-123” 的输出。(这是进一步未定义的行为,因为不应与 一起使用,并且 C 标准没有定义这种不匹配会发生什么。但是,将 的位解释为 an 是很常见的。unsignedprintf%dint%dunsigned intunsigned intint

2赞 Ted Lyngmo 11/9/2023 #2
  • printf("%d %d\n", u1, u2);
    这具有未定义的行为,因为您使用的是 而不是 。%d%u

  • unsigned u1 = -123.123f;
    这具有未定义的行为,因为 的截断部分不能用 表示。来自 C23 草案:-123.123unsigned

    6.3.1.4 实浮点数和整数

    1. 当标准浮点型的有限值转换为除 之外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。65)bool

    65)将整数类型的值转换为无符号类型时执行的剩余操作不必是 当实际浮动类型的值转换为无符号类型时执行。因此,可移植实际浮点值的范围是 (−1, Utype_MAX + 1)。

  • 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 .
unsigned0u - 1 == UINT_MAX0u - 123UINT_MAX - 122