类型转换:double to char:多个问题

Type casting: double to char: multiple questions

考虑这段代码:

#include <stdio.h>

int main(void) 
{
    /* TEST 1 */
    double d = 128;
    char ch = (char)d;
    printf("%d\n", ch);

    /* TEST 2 */
    printf("%d\n", (char)128.0);

    /* TEST 3 */
    char ch1 = (char)128.0;
    printf("%d\n", ch1);
    return 0;
}

结果:

        gcc*  clang*  cl*
TEST 1  -128  -128    -128
TEST 2  127   0       -128
TEST 3  127   -2      -128

* latest version

问题:

  1. 为什么测试结果不同(不包括 cl)?
  2. 为什么编译器之间的结果不同(不包括 TEST 1)?
  3. 如果是 UB/IB,UB/IB 到底在哪里?标准怎么说?
  4. [额外问题] 为什么 clang 表现出如此不同的行为?这些 0-2 来自哪里?

CHAR_MAX == 127 时,(char)128.0 未定义的行为 (UB)。

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. C17dr § 6.3.1.4 1

由于整数溢出,这不是 UB。由于转换规则,它是 UB。

AS @chux 表示 (char)128.0 是一个 UB。 gcc 因为该示例的简单性检测到此 UB,而是将 CHAR_MAX 作为最大最接近的有符号数。

但是如果你稍微混淆一下它就不会这样了(转换为 int 不是 UB,gcc 也检测不到下一个转换 UB)。

int main(void) 
{
    volatile char x = (char)128.0;
    volatile char y = (char)(int)128.0;

    printf("%d %d\n", x, y);
}

和代码(有趣的部分):

        mov     BYTE PTR [rsp+14], 127
        mov     BYTE PTR [rsp+15], -128

https://godbolt.org/z/xG3jUy

顺便说一句,这个 gcc 行为很久以前就被讨论过了,很多人(包括我)都反对它。但是 gcc 开发人员决定走这条路。