使用减法将有符号数转换为无符号数

5

最近我决定大量阅读计算机科学书籍,以更好地为未来做准备。

目前我正在阅读关于有符号数转换为无符号数的内容。我理解了大部分内容(希望它最终变得更容易),但是在以下问题上遇到了困难(在32位中):

-2147483647-1U < -2147483647

根据书中的说法,这个式子的结果为true。有一点让我仍然感到困惑,因为我不知道为什么会得到这个结果。

根据我的理解,在这个计算中,由于第一个数字被强制转换成无符号类型,所以它们都被转换成无符号值。因此,在减法运算后,第一个数字变成了-2147483648,然后被转换成无符号数,或者说无符号转换是在减法之前发生的吗?

对于篇幅冗长的帖子,非常抱歉,我只是试图理解这个问题。

谢谢!


这是由语言定义的。既然你标记了C,那么你最喜欢的C书籍有什么不清楚的地方吗?你在标准中没有找到什么?对于C而言,如果没有细节,这可能是错误的。而且没有任何转换!转换/强制转换并不等同于转型。 - too honest for this site
最后,我们又回到了那些毫无意义的评论... - user2371524
@Olaf 我原以为这与C语言有关,因为这本书使用C语言来讲解计算机科学概念。如果这样说不通,请原谅我。这本书是《深入理解计算机系统》。他提到添加后缀'u'可以创建无符号常量。 - Rixium
抛开那本书吧。C语言的概念并不适用于所有编程领域。如果你想学习C语言,最好找一本正规的C语言书籍;如果这本书在这里就出现错误了,我不会相信它所包含的任何内容。 - too honest for this site
4个回答

7
因此,在减法运算后,第一个数字是-2147483648。但实际上不是这样的。使用“-2147483647-1U”,首先发生对“unsigned”的转换。在混合“int/unsigned”数学中,“int”转换为“unsigned”。从“int”中减去“unsigned”会导致一个“unsigned”,而“unsigned”永远不会是负数。
-2147483647-1U < -2147483647

假设是32位或更宽的无符号/整型。 -2147483647-1U是一个int减去一个unsigned,所以-2147483647被转换为unsigned 2147483649,两者之间的差是unsigned 2147483648。现在我们将一个unsigned与一个int比较,所以int被转换为unsigned 2147483649。左边小于右边,因此结果为true。
[编辑]
假设比32位窄的unsigned/int,但long使用常见的二进制补码编码。经常在2017年的嵌入式8/16位处理器中看到。 -2147483647-1U是一个长整型减去一个比其窄的unsigned,所以-2147483647仍然是一个long,并且1U被转换为int 1,两者之间的差是long -2147483648。现在一个long被与另一个long比较。左边小于右边,因此结果为true。

1
谢谢。在做减法之前计算无符号值确实是有意义的。我在纸上用一个较小的值进行了计算,现在我可以看到它是如何工作的。谢谢。 - Rixium

6
第一个数字在减法运算后转换为无符号数,因此结果为-2147483648,还是在减法之前进行了无符号转换? 后者是正确的。根据C11 Standard "6.3.1.8 Usual arithmetic conversions":

...否则,如果具有无符号整数类型的操作数的等级大于或等于另一个操作数的类型的等级,则带有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型。

因此,所有操作数,包括用于减法和比较的操作数,都将转换为无符号数。 附言:为了完整起见,转换过程在"6.3.1.3 Signed and unsigned integers"中描述:
如果新类型是无符号的,那么该值将通过重复添加或减去比新类型中可以表示的最大值还多一个的值,直到该值在新类型的范围内为止进行转换。注意:此答案假设int为32位长。如果小于32位,则负常数-2147483647将具有比unsigned int更高的等级long类型,然后上述内容将不适用,并且不会执行任何转换(感谢@Olaf指出)。

1
对于初学者来说,IMO标准过于封闭和难以理解。根据我在黑客空间的经验 - 阅读标准会比不太精确但更易懂的答案更加混乱。 - 0___________
3
我同意你的观点,但是在回答时需要提供可信的参考资料。我们不只是说“去读标准”,而是提供相关部分,并在必要时尝试解释它,如果这特别晦涩难懂的话(我认为这里不是这种情况)。 - Eugene Sh.
1
我暂时不会阅读整个文本,但你发布的内容很相关并帮助我理解。谢谢。 - Rixium
1
好的...可能会很“长”...我想我只能在答案中添加一个假设,而不是对其进行扩展。 - Eugene Sh.
1
@Olaf 我猜如果问题陈述为 INT_MIN-1U < INT_MIN,这个问题就可以解决了。 - Eugene Sh.
显示剩余10条评论

2

两者都被提升为无符号数,-2147483647-1U == 0x8000000(unsigned)-2147483647 == 0x80000001

0x8000000 < 0x8000001true

有符号数也是一样的。

-2147483647-1 == `-2147483648`
-2147483648 < -2147483647

1

这其实很简单。根据常规算术转换,如果一个int在运算中遇到了一个unsigned int,那么操作数都会被转换为unsigned int

重复加上或减去新类型中可以表示的最大值加一所形式化的转换描述了自然有符号/无符号重新解释在二进制补码算术中的工作方式(这是硬件中表示有符号整数的最常见方式,虽然不是唯一的方法)。

32位有符号数的二进制补码算术工作方式如下:

  • 如果最高位被设置,则代表-2^31
  • 其余位代表它们自己

-2147483647因此等于2^31+1 == 0b10000000000000000000000000000001。 现在,由于它与1U0b00000000000000000000000000000001)进行减法运算,您应该通过反复添加或减去UINT32_MAX+1 == 2^32将其转换为uint32_t数字,这样在溢出后将得到相同的二进制表示(我想规则是这样制定的,以便可以在2的补码(最常见)平台上直接重新解释位),从中减去1U将直接产生 0b10000000000000000000000000000000 == 2^31,小于2^31+1


将有符号整数重新解释为无符号整数仅适用于2的补码表示法。该OP并没有明确表达他是关于这个问题的。由于他所使用的书籍是关于一般计算机科学,而C语言允许使用另外两种表示方式,因此事情并不那么简单。 - too honest for this site
@Olaf 哦,我已经用一般规则开始了回答。我只是使用二进制补码示例来解释规则。 - Petr Skocik
你应该指出这一点,并且它不是唯一的表示方式 - 即使对于C语言也是如此。 - too honest for this site

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接