如何解决无符号整数的上溢和下溢
假设,我正在尝试减去2个无符号整数:
247 = 1111 0111
135 = 1000 0111
如果我们减去这两个二进制数字,我们将得出= 0111 0000
这是下溢吗,因为我们现在只需要7位?或如何运作?
解决方法
只要c = a - b
大于b
,就会发生无符号减法a
中的下溢。
但是,这有点像是循环定义,因为有多少种机器执行a < b
比较是通过使用环绕算术减去操作数,然后根据两个操作数和结果检测溢出。 / p>
还要注意,在C语言中我们不会谈论“溢出”,因为没有错误条件:C无符号整数提供了通常在硬件中发现的环绕算法。
因此,考虑到我们具有环绕算法,我们可以检测出减法是否发生了环绕(或溢出,取决于角度)。
我们需要的是a
,b
和c
中的最高有效位。我们称它们为A
,B
和C
。根据这些,溢出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
中的溢出发生在以下情况:
-
a
的msb为0,b
的msb为1; - 或
a
的msb为0,而c
的msb为1; - 或
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
现在是科学版本:上面的评论仅代表正在发生的事情的数学模型。为了更好地了解实际发生的情况,适用以下规则:
- 整数提升:
小于 int 的整数类型在操作时被提升 对他们进行。如果原始类型的所有值都可以 表示为 int,较小类型的值被转换为 一个整数;否则,它被转换为一个无符号整数。
所以当计算机执行 n = 255; n = n + 1;
时,内存中实际发生的事情是这样的:
首先,右侧被评估为一个 int(有符号),因为根据整数提升规则,结果适合一个有符号 int。所以表达式的右边变成了二进制:0b00000000000000000000000011111111 + 0b00000000000000000000000000000001 = 0b00000000000000000000000100000000
(一个 32 位整数)。
- 截断
32 位 int 在被赋值时丢失了最高有效的 24 位 回到 8 位数字。
所以,当 0b00000000000000000000000100000000
赋给变量 n 时,它是一个无符号字符,32 位值被截断为 8 位值(只复制最右边的 8 位)=> n变成 0b00000000
。
每次操作都会发生同样的事情。右侧的表达式计算为有符号整数,然后将其截断为 8 位。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。