减去两个无符号的32位整数并分配给64位有符号整数

时间:2013-04-03 18:26:41

标签: c syntax integer-arithmetic

目标是得到(-1)作为最终结果。

#include <stdio.h>
#include <stdint.h>

int main()
{

    uint32_t us32Var1, us32Var2;
    int32_t s32Var1;
    int64_t s64Var1;

    us32Var1 = 0;
    us32Var2 = 1;
    printf("(1) us32Var1 = %u(0x%08x), us32Var2 = %u(0x%08x)\n", us32Var1, us32Var1, us32Var2, us32Var2);

    s32Var1 = us32Var1 - us32Var2; //0xffffffff (-1)
    printf("(2) s32Var1 = us32Var1 - us32Var2, s32Var1 = %d(0x%08x)\n", s32Var1, s32Var1);

    s64Var1 = us32Var1 - us32Var2;  //0x00000000ffffffff  (expect it's -1, but the result > 0)
    printf("(3) s64Var1 = us32Var1 - us32Var2, s64Var1 = %ld(0x%016lx)\n", s64Var1, s64Var1);

    s64Var1 = (int64_t)(us32Var1 - us32Var2);  //0x00000000ffffffff  (expect it's -1, but the result > 0)
    printf("(4) s64Var1 = (int64_t)(us32Var1 - us32Var2), s64Var1 = %ld(0x%016lx)\n", s64Var1, s64Var1);

    s64Var1 = (int64_t)us32Var1 - us32Var2;  //0xffffffffffffffff  (-1)
    printf("(5) s64Var1 = (int64_t)us32Var1 - us32Var2, s64Var1 = %ld(0x%016lx)\n", s64Var1, s64Var1);

    us32Var1 = -1; //us32Var1 = 0xffffffff, UINT32_MAX
    s64Var1 = (int64_t)us32Var1; //0x00000000ffffffff  (expect it's -1, but the result > 0)
    printf("(6) s64Var1 = (int64_t)us32Var1, s64Var1 = %ld(0x%016lx)\n", s64Var1, s64Var1);

    s64Var1 = (int32_t)us32Var1; //0xffffffffffffffff  (-1 !)
    printf("(7) s64Var1 = (int32_t)us32Var1, s64Var1 = %ld(0x%016lx)\n", s64Var1, s64Var1);
    return 0;
}

对于上述程序(每行都有编号),有人可以解释

  • 为什么(2)按预期工作,但不是(3)? ALU的算术或微步骤是什么?
  • (4)和(5)之间有什么区别
  • 为什么(7)工作而(6)失败

2 个答案:

答案 0 :(得分:2)

(2)有效,但可能不是你的想法。 us32Var1-us32Var1将产生无符号值0xFFFFFFFFU(模运算),对s32Var1的赋值将其转换为带符号的32位值,即-1。在(3)中,赋值也转换它,但现在转换为64位有符号值,并且大到足以包含0xFFFFFFFFU,因此它保持正值。 (4)和(5)之间的区别在于,在(4)中,减法发生在32位,然后转换为64位。在(5)中,其中一个操作数首先被转换为64位,因此减法为64位,因此另一个操作数也被隐式转换为64位。 (6)类似于(2),32位中的带符号值-1在赋值给us32Var1时被转换为无符号32位,因此结果为0xFFFFFFFFU这很容易拟合在s64Var1,所以它保持积极。在(7)中,0xFFFFFFFFU首先转换为带符号的32位,因此为-1,然后将其分配给带符号的64位,从而生成-1

我希望这会有所帮助。

所以回答下面的问题。在(3)中,减法仍以32位无符号进行,因此结果为0xFFFFFFFFU,因为-1无法用无符号表示。这个32位无符号结果被转换为带符号的64位,它可以精确地表示0xFFFFFFFFU,因此就是结果。

对于(7),赋值确实执行符号扩展,因为右侧是32位且为负。请注意,变量us32Var1包含(当然)正值,但它会转换为32位有符号值,不能代表0xFFFFFFFFU,因此它会被-1转换为{{1}}铸造。

答案 1 :(得分:2)

  • 为什么(2)按预期工作,但不是(3)? ALU的算术或微步骤是什么?

嗯,它按照期望的方式工作。计算无符号的 32 结果,然后将无符号值转换为有符号,因此它自然是零扩展。

  • (4)和(5)之间有什么区别?

(4)与我上面描述的相同,并且(5)最终计算0LL-1LL,其实际上产生~0LL。它可能会产生什么其他结果?值得注意的是,在机器内部,有符号和无符号数字之间没有太大区别。完全相同的ALU用于有符号和无符号操作。除了少数例外情况,签名 vs 无符号区别是在解释结果时以及在决定是否签名扩展时所做的区分,而不是在实际执行算术ALU时进行的区分操作

  • 为什么(7)工作而(6)失败?

我不确定我会说(6)失败,确切地说。您将无符号值转换为有符号值,因此它自然地对其进行零扩展。在(7)中,您将带符号的 32 值转换为64位,因此它自然是符号扩展的。