微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

无符号整数的上溢和下溢

如何解决无符号整数的上溢和下溢

假设,我正在尝试减去2个无符号整数:

247 = 1111 0111
135 = 1000 0111

如果我们减去这两个二进制数字,我们将得出= 0111 0000

这是下溢吗,因为我们现在只需要7位?或如何运作?

解决方法

只要c = a - b大于b,就会发生无符号减法a中的下溢。

但是,这有点像是循环定义,因为有多少种机器执行a < b比较是通过使用环绕算术减去操作数,然后根据两个操作数和结果检测溢出。 / p>

还要注意,在C语言中我们不会谈论“溢出”,因为没有错误条件:C无符号整数提供了通常在硬件中发现的环绕算法。

因此,考虑到我们具有环绕算法,我们可以检测出减法是否发生了环绕(或溢出,取决于角度)。

我们需要的是abc中的最高有效位。我们称它们为ABC。根据这些,溢出V的计算如下:

A B C | V
------+--
0 0 0 | 0
0 0 1 | 1
0 1 0 | 1
0 1 1 | 1
1 0 0 | 0
1 0 1 | 0
1 1 0 | 0
1 1 1 | 1

这简化为

A'B + A'C + BC

换句话说,无符号减法c = a - b中的溢出发生在以下情况:

  1. a的msb为0,b的msb为1;
  2. a的msb为0,而c的msb为1;
  3. b的msb为1,而c的msb也为1。

减去247-135 = 112显然不会溢出,因为247大于135。应用上面的规则,A = 1,B = 0和C =0。表的1 1 0行具有0 in V栏:无溢出。

,

通常,“下溢”是指计算的理想数学结果低于该类型可以表示的结果。如果在无符号算术中从5中减去7,则理想的数学结果将为-2,但无符号类型不能表示-2,因此运算会下溢。或者,在可以表示从-128到+127的数字的八位带符号类型中,从-100减去100理想情况下会产生-200,但这不能用该类型表示,因此操作会下溢。

在C中,无符号算术被称为不会下溢或上溢,因为C标准定义了使用模算术而不是实数算术执行的操作。例如,使用32位无符号算术,从5中减去7将产生4,294,967,294(以十六进制表示,FFFFFFFE 16 ),因为它包装了模2 32 = 4,296。尽管如此,人们在讨论这些操作时仍可能使用术语“下溢”或“上溢”,意在指数学问题,而不是定义的C行为。

换句话说,对于您用于算术的任何类型,该类型都可以表示一些下限 L 和一些上限 U 。如果运算的理想数学结果小于 L ,则运算会下溢。如果某个操作的理想数学结果大于 U ,则该操作会溢出。 “下溢”和“上溢”表示操作已超出该类型的范围。 “溢出”也可以用来指代该类型的任何边界,包括低端。

这并不意味着需要更少的位来表示结果。从11110111 2 中减去10000111 2 时,结果01110000 2 = 1110000 2 在范围内,因此没有上溢或下溢。它需要更少的比特来表示这一事实是不相关的。

(注意:对于整数运算,相对于绝对界限 L U 定义“下溢”或“上溢”。对于浮点运算,这些术语含义有些不同:可以相对于结果的大小(忽略符号)来定义它们,而相对于格式的有限非零范围来定义它们。浮点格式可以表示0,然后从0到格式可以表示的最小非零数字之间的某些结果,即使在技术上处于可表示数字范围(从0到无穷大)内,也被认为是下溢。同样,某些结果超出最大可表示有限数,即使它们在可表示范围内,也被认为是溢出的,因为它们小于无穷大。)

,

长话短说,这就是当你有:

unsigned char n = 255; /* highest possible value for an unsigned char */

n = n + 1; /* now n is "overflowing" (although the terminology is not correct) to 0 */
printf("overflow: 255 + 1 = %u\n",n);
   
n = n - 1; /* n will now "underflow" from 0 to 255; */
printf("underflow: 0 - 1 = %u\n",n);
   
n *= 2; /* n will now be (255 * 2) % 256 = 254; 
        /* when the result is too high,modulo with 2 to the power of 8 is used */ 
        /* for an 8 bit variable such as unsigned char; */
printf("large overflow: 255 * 2 = %u\n",n);   
   
n = n * (-2) + 100; /* n should now be -408 which is 104 in terms of unsigned char. */
                       /* (Logic is this: 408 % 256 = 152; 256 - 152 = 104) */
printf("large underflow: 255 * 2 = %u\n",n);

结果是(使用 gcc 11.1 编译,标志 -Wall -Wextra -std=c99):

overflow: 255 + 1 = 0
underflow: 0 - 1 = 255
large overflow: 255 * 2 = 254
large underflow: 255 * 2 = 104

现在是科学版本:上面的评论仅代表正在发生的事情的数学模型。为了更好地了解实际发生的情况,适用以下规则:

  1. 整数提升:

小于 int 的整数类型在操作时被提升 对他们进行。如果原始类型的所有值都可以 表示为 int,较小类型的值被转换为 一个整数;否则,它被转换为一个无符号整数。

所以当计算机执行 n = 255; n = n + 1; 时,内存中实际发生的事情是这样的: 首先,右侧被评估为一个 int(有符号),因为根据整数提升规则,结果适合一个有符号 int。所以表达式的右边变成了二进制:0b00000000000000000000000011111111 + 0b00000000000000000000000000000001 = 0b00000000000000000000000100000000(一个 32 位整数)。

  1. 截断

32 位 int 在被赋值时丢失了最高有效的 24 位 回到 8 位数字。

所以,当 0b00000000000000000000000100000000 赋给变量 n 时,它是一个无符号字符,32 位值被截断为 8 位值(只复制最右边的 8 位)=> n变成 0b00000000

每次操作都会发生同样的事情。右侧的表达式计算为有符号整数,然后将其截断为 8 位。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。