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

时间:2015-07-09 21:13:26

标签: 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;
}

输出是:

-200
-100
8589934592.000000
Program ended with exit code: 0

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

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:5)

您认为a - b为负数的假设完全不正确。

由于ab具有unsigned int类型,因此这两个变量的所有算术运算都在unsigned int类型的域中执行。这同样适用于混合的“unsigned intint”算术。这些操作实现模运算,模数等于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值是违法的。出于这个原因,代码的行为是 undefined 。您在输出中看到的-100值只是该未定义行为的一种表现形式。这个输出正式毫无意义,即使它第一眼看上去是“正确的”。

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

有人可能会争辩说,您打印的最后一个值是唯一一个正确反映无符号算术属性的值,即它是“自然地”从a - b的实际结果中得出的。

答案 1 :(得分:0)

你在printf中得到负数,因为你已要求它打印带有%d的有符号整数。如果要查看最终得到的实际值,请使用%u。这也将告诉你如何最终得到浮点乘法的输出。