使用unsigned int,float数据类型和乘法的奇怪

fac*_*818 1 c unsigned unsigned-integer

我不擅长C语言,只是遇到了一个我不明白的问题.代码是:

int main()
{
    unsigned int a = 100;
    unsigned int b = 200;
    float c = 2;

    int result_i;
    unsigned int result_u;
    float result_f;

    result_i = (a - b)*2;
    result_u = (a - b);
    result_f = (a-b)*c;

    printf("%d\n", result_i);
    printf("%d\n", result_u);
    printf("%f\n", result_f);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

-200
-100
8589934592.000000
Program ended with exit code: 0
Run Code Online (Sandbox Code Playgroud)

因为(ab)是负的,而a,b是无符号的int类型,(ab)是微不足道的.乘以浮点类型数c后,结果为8589934592.000000.我有两个问题:

首先,为什么在将int类型数字2相乘并分配给int类型数字后,结果是非平凡的?

第二,为什么result_u是非平凡的,即使(ab)是负数,而result_u是无符号的int类型?

我使用Xcode测试此代码,编译器是默认的APPLE LLVM 6.0.

谢谢!

AnT*_*AnT 5

你的假设a - b是负面的是完全错误的.

由于ab具有unsigned int这两个变量的类型的所有算术运算都是在unsigned int类型的域中执行的.这同样适用于混合" unsigned intwith int"算法.这些操作实现模运算,模数等于UINT_MAX + 1.

这意味着表达式a - b产生类型的结果unsigned int.它是一个大的正值等于UINT_MAX + 1 - 100.在具有32位的典型平台上,int它是4294967296 - 100 = 4294967196.

表达式(a - b) * 2也会产生类型的结果unsigned int.它也是一个很大的正值(UINT_MAX + 1 - 100乘以2并取模UINT_MAX + 1).在一个典型的平台上4294967096.

后一个值对于类型来说太大了int.这意味着当您将其强制转换为变量时result_i,会发生有符号整数溢出.赋值时有符号整数溢出的结果是实现定义的.在你的情况下result_i最终成为-200.它看起来"正确",但语言无法保证这一点.(尽管可能会通过您的实施来保证.)

变量result_u接收正确的无符号结果 - 正值UINT_MAX + 1 - 100.但是您使用%d格式说明符打印该结果printf,而不是正确的%u.打印unsigned int不符合int使用%d说明符范围的值是非法的.出于这个原因,代码的行为是未定义的.-100您在输出中看到的值只是该未定义行为的一种表现形式.这种输出正式毫无意义,即使它在第一眼看上去是"正确的".

最后,变量result_f接收(a-b)*c表达式的"正确"结果,计算时没有溢出,因为乘法是在float域中执行的.你看到的是我上面提到的大的正值,乘以2.它可能会舍入到float类型的精度,这是实现定义的.确切的价值是4294967196 * 2 = 8589934392.

人们可以争辩说,你打印的最后一个值是唯一一个正确反映无符号算术属性的值,即它是"自然地"从实际结果中得出的a - b.