首页 > 解决方案 > C是整数数学等价于无符号数学?

问题描述

那么我可以将这些值转换为无符号值,进行操作并转换回来,并得到相同的结果吗?我想这样做是因为无符号整数可能溢出,而有符号整数不能。

标签: cmathunsigned

解决方案


无符号整数算术在 C 术语中不会溢出,因为它被定义为对模 2 N进行包装,其中N是根据 C 2018 6.2.5 9 操作的无符号类型中的位数:

... 涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数字为模减少。

对于其他类型,如果发生溢出,则行为未由 C 标准定义,每 6.5 5:

如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。请注意,不仅结果是未定义的;程序的整个行为是未定义的。它可能会给出您不期望的结果,它可能会陷入陷阱,或者它可能会执行与您期望的完全不同的代码。

关于你的问题:

那么我可以将这些值转换为无符号值,执行操作并返回,并获得相同的结果吗?

我们有两个问题。首先,考虑a + b给定int a, b;。如果a + b溢出,则行为未由 C 标准定义。所以我们不能说转换为unsigned、添加和转换回是否int会产生相同的结果,因为一开始没有定义的结果a + b

其次,根据 C 6.3.1.3,转换回部分是实现定义的。考虑int c = (unsigned) a + (unsigned) b;,它将unsigned总和隐式转换为 anint以存储在c. 第 1 段告诉我们,如果 sum 的值可以表示为int,则它是转换的结果。但是第 3 段告诉我们如果值在 中不可表示会发生什么int

否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。

例如,GCC将结果定义为包装模 2 N的结果。因此,对于,GCC将产生与包装模 2 Nint c = (unsigned) a + (unsigned) b;相同的结果。但是,GCC 不保证后者。优化时,GCC 预计不会发生溢出,这可能导致它消除程序允许发生溢出的任何代码分支。(GCC 可能有一些关于溢出处理的选项。)int c = a + b;a + b

此外,即使有符号算术和无符号算术都进行了换行,使用无符号值执行操作并转换回来在数学上也不会产生与使用有符号值执行操作相同的结果。例如,考虑-3/2. 结果int为 -1。但如果-3转换为 32 位无符号,则结果值为 2 32 -3,然后(int) ((unsigned) -3 / (unsigned) 2)是 2 -31 -2 = 2,147,483,646。


推荐阅读