使用 Assembly 实现纠正溢出情况
Correcting overflow situations with Assembly implementation
上下文:
我目前正在学习使用 Intel IA32 x86 处理器的 Linux 发行版的汇编,我一直在努力了解如何处理溢出问题。
我相信我理解它的理论,但我还没有设法为溢出情况实施解决方案。
我在网上搜索过,我找到的页面解释了标志系统并且 how/when 它们激活了。那不是我感兴趣的。
我想知道如何 "solve" 它。
实际上,当我写这篇文章的时候,我仔细地、慢慢地思考,我相信我理解了解决方案。所以现在我实际上只想知道我是在直想还是有更好的方法。
问题:
在以下情况下可能会发生溢出:
- 1st - 添加两个正数 || (+) + (+) = (-)
- 2nd - 添加两个负数 || (-) + (-) = (+)
- 3rd - 负数减去正数 || (+) - (-) = (-)
- 4 - 正数减去负数 || (-) - (+) = (+)
为简单起见,让我们考虑两个带符号的字符(1 字节)。我知道结果将需要更多位,因此让我们将结果视为 short int(2 字节).
数据:
op1 = 0x7F # Bit representation: 0111 1111 Decimal representation: +127
op2 = 0x01 # Bit representation: 0000 0001 Decimal representation: + 1
op3 = 0x80 # Bit representation: 1000 0000 Decimal representation: -128
op4 = 0xff # Bit representation: 1111 1111 Decimal representation: - 1
示例 A:
movl [=11=], %eax # Set all bits to 0
movb op1, %al # Move op1 to the al register
addb op2, %al # Adding two positives that activates the Overflow flag
# Deactivates the Carry Flag
# Current bit representation in ax: 0000 0000 1000 0000
# Expected bit representation in ax: 0000 0000 1000 0000
# The simple fact of consider the short int (which contains 0s) fixes the issue. || (+127) + (+1) = (+128)
ret # Returns ax
示例 B:
movl [=12=], %eax # Set all bits to 0
movb op3, %al # Move op3 to the al register
addb op4, %al # Adding two negative numbers that activates the Overflow flag
# Activates Carry Flag
# Current bit representation in ax: 0000 0000 0111 1111
# Expected bit representation in ax: 1111 1111 0111 1111
notb %ah # The "not" instruction on the most significative byte resolves the issue since it inverts all bits. || (-128) + (-1) = (-129)
ret # Returns ax
示例 C:
movl [=13=], %eax # Set all bits to 0
movb op1, %al # Move op1 to the al register
subb op4, %al # Subtracting a negative number to a positive number that activates the Overflow flag
# Activates the Carry Flag
# Current bit representation in ax: 0000 0000 1000 0000
# Expected bit representation in ax: 0000 0000 1000 0000
# The simple fact of consider the short int (which contains 0s) fixes the issue. || (+127) - (-1) = (+128)
ret # Returns ax
示例 D:
movl [=14=], %eax # Set all bits to 0
movb op3, %al # Move op3 to the al register
subb op2, %al # Subtracting a positive number to a negative number that activates the Overflow flag
# Deactivates Carry Flag
# Current bit representation in ax: 0000 0000 0111 1111
# Expected bit representation in ax: 1111 1111 0111 1111
notb %ah # The "not" instruction on the most significative byte resolves the issue since it inverts all bits. || (-128) - (+1) = (-129)
ret # Returns ax
对于我上面所说的一切,我总结出以下两个功能:
添加:
addition:
movl [=15=], %eax # Set all bits to 0
movb opX, %al # Move opX to the al register
addb opY, %al # Adding two numbers
jc overflow # Jumps if Carry Flag is on
end:
ret # Returns ax
overflow:
notb %ah # Inverts most significative bits
jmp end # Jumps back to the end of the function
对于减法:
subtraction:
movl [=16=], %eax # Set all bits to 0
movb opX, %al # Move opX to the al register
subb opY, %al # Subtracting two numbers
jc end # Jumps if Carry Flag is on
notb %ah # Inverts most significative bits
end:
ret # Returns ax
我相信即使使用不会导致溢出的数字,这些函数也能正常工作。
问题:
1st:我上面说的有没有错?
2nd: 如果我遇到了,在哪里以及如何解决?如果我没有,有没有更好的方法来解决这个问题?
3rd:这个会不会干扰其他不会导致溢出的加减法?
4th:为什么我没有在任何地方使用溢出标志?
您可能有点忽略了溢出通常是想要的行为这一点(由于简单+廉价的硬件实现加上高性能)。
即。如果程序员希望得到像 150 + 150 这样的数字,则使用 16 位算术取决于程序员,并且只有当他们知道结果将在 0..255 或 -128..+127 内,或者如果他们不知道时,才继续使用 8 位算术不要介意溢出时截断的结果(在某些情况下是有效的使用模式)。
你的 4->8 扩展没有多大意义,通常有 32b 个整数就足够了,根本不会溢出。当出现 2+GiB RAM 和文件时,该假设确实有害,使一些安装程序报告可用 RAM 为 -2GB 并要求更多 :),或仅复制 3GiB 文件的一部分。
所以第二个例子应该改为(我的 AT&T 语法不是很好,对于可能的语法错误很抱歉),指望你的数据不会随着有效输入数据溢出,作为有效数据适合 16b 结果:
movsbw op3, %ax # Move op3 to the ax register, sign extend it from 8b to 16b
movsbw op4, %bx # sign-extend op4 to 16b as well
addw %bx, %ax # Adding two negative numbers, not caring about OF/CF, expecting valid result
# Current bit representation in ax: 1111 1111 0111 1111
# note the eax is: ???? ???? ???? ???? 1111 1111 0111 1111
# high 16b are undefined, and if you would clear eax, they would be 0 anyway
ret
照顾CF/OF会大大降低性能,并且仅在您确实必须验证计算的特殊情况下使用。大多数 SW 愉快地生活在 "garbage in, garbage out" 前提下(通常效果很好),并且只为 valid/expected 输入产生 valid/expected 输出。
进行所有计算 "safe/correct" 有点毫无意义(或者更确切地说 "expensive"),因为计算机的资源非常有限,而且无论如何您都无法使用计算机处理大数。本机 64b CPU 可以使用 64 或 128b 整数进行算术运算,除此之外,您可以使用可用内存大小的整数实现非本机操作,但迟早(可能非常晚,考虑到 32GiB 的操作内存)你会遇到点,在那里你会溢出足够大的整数(本例中为 28e32G)。自然地,你已经完成了 264/128/256+ 值甚至更快,这对于整数来说甚至不是那么多,尽管对于普通人来说甚至只是 232通常就够了。
上下文:
我目前正在学习使用 Intel IA32 x86 处理器的 Linux 发行版的汇编,我一直在努力了解如何处理溢出问题。
我相信我理解它的理论,但我还没有设法为溢出情况实施解决方案。
我在网上搜索过,我找到的页面解释了标志系统并且 how/when 它们激活了。那不是我感兴趣的。 我想知道如何 "solve" 它。
实际上,当我写这篇文章的时候,我仔细地、慢慢地思考,我相信我理解了解决方案。所以现在我实际上只想知道我是在直想还是有更好的方法。
问题:
在以下情况下可能会发生溢出:
- 1st - 添加两个正数 || (+) + (+) = (-)
- 2nd - 添加两个负数 || (-) + (-) = (+)
- 3rd - 负数减去正数 || (+) - (-) = (-)
- 4 - 正数减去负数 || (-) - (+) = (+)
为简单起见,让我们考虑两个带符号的字符(1 字节)。我知道结果将需要更多位,因此让我们将结果视为 short int(2 字节).
数据:
op1 = 0x7F # Bit representation: 0111 1111 Decimal representation: +127
op2 = 0x01 # Bit representation: 0000 0001 Decimal representation: + 1
op3 = 0x80 # Bit representation: 1000 0000 Decimal representation: -128
op4 = 0xff # Bit representation: 1111 1111 Decimal representation: - 1
示例 A:
movl [=11=], %eax # Set all bits to 0
movb op1, %al # Move op1 to the al register
addb op2, %al # Adding two positives that activates the Overflow flag
# Deactivates the Carry Flag
# Current bit representation in ax: 0000 0000 1000 0000
# Expected bit representation in ax: 0000 0000 1000 0000
# The simple fact of consider the short int (which contains 0s) fixes the issue. || (+127) + (+1) = (+128)
ret # Returns ax
示例 B:
movl [=12=], %eax # Set all bits to 0
movb op3, %al # Move op3 to the al register
addb op4, %al # Adding two negative numbers that activates the Overflow flag
# Activates Carry Flag
# Current bit representation in ax: 0000 0000 0111 1111
# Expected bit representation in ax: 1111 1111 0111 1111
notb %ah # The "not" instruction on the most significative byte resolves the issue since it inverts all bits. || (-128) + (-1) = (-129)
ret # Returns ax
示例 C:
movl [=13=], %eax # Set all bits to 0
movb op1, %al # Move op1 to the al register
subb op4, %al # Subtracting a negative number to a positive number that activates the Overflow flag
# Activates the Carry Flag
# Current bit representation in ax: 0000 0000 1000 0000
# Expected bit representation in ax: 0000 0000 1000 0000
# The simple fact of consider the short int (which contains 0s) fixes the issue. || (+127) - (-1) = (+128)
ret # Returns ax
示例 D:
movl [=14=], %eax # Set all bits to 0
movb op3, %al # Move op3 to the al register
subb op2, %al # Subtracting a positive number to a negative number that activates the Overflow flag
# Deactivates Carry Flag
# Current bit representation in ax: 0000 0000 0111 1111
# Expected bit representation in ax: 1111 1111 0111 1111
notb %ah # The "not" instruction on the most significative byte resolves the issue since it inverts all bits. || (-128) - (+1) = (-129)
ret # Returns ax
对于我上面所说的一切,我总结出以下两个功能:
添加:
addition:
movl [=15=], %eax # Set all bits to 0
movb opX, %al # Move opX to the al register
addb opY, %al # Adding two numbers
jc overflow # Jumps if Carry Flag is on
end:
ret # Returns ax
overflow:
notb %ah # Inverts most significative bits
jmp end # Jumps back to the end of the function
对于减法:
subtraction:
movl [=16=], %eax # Set all bits to 0
movb opX, %al # Move opX to the al register
subb opY, %al # Subtracting two numbers
jc end # Jumps if Carry Flag is on
notb %ah # Inverts most significative bits
end:
ret # Returns ax
我相信即使使用不会导致溢出的数字,这些函数也能正常工作。
问题:
1st:我上面说的有没有错?
2nd: 如果我遇到了,在哪里以及如何解决?如果我没有,有没有更好的方法来解决这个问题?
3rd:这个会不会干扰其他不会导致溢出的加减法?
4th:为什么我没有在任何地方使用溢出标志?
您可能有点忽略了溢出通常是想要的行为这一点(由于简单+廉价的硬件实现加上高性能)。
即。如果程序员希望得到像 150 + 150 这样的数字,则使用 16 位算术取决于程序员,并且只有当他们知道结果将在 0..255 或 -128..+127 内,或者如果他们不知道时,才继续使用 8 位算术不要介意溢出时截断的结果(在某些情况下是有效的使用模式)。
你的 4->8 扩展没有多大意义,通常有 32b 个整数就足够了,根本不会溢出。当出现 2+GiB RAM 和文件时,该假设确实有害,使一些安装程序报告可用 RAM 为 -2GB 并要求更多 :),或仅复制 3GiB 文件的一部分。
所以第二个例子应该改为(我的 AT&T 语法不是很好,对于可能的语法错误很抱歉),指望你的数据不会随着有效输入数据溢出,作为有效数据适合 16b 结果:
movsbw op3, %ax # Move op3 to the ax register, sign extend it from 8b to 16b
movsbw op4, %bx # sign-extend op4 to 16b as well
addw %bx, %ax # Adding two negative numbers, not caring about OF/CF, expecting valid result
# Current bit representation in ax: 1111 1111 0111 1111
# note the eax is: ???? ???? ???? ???? 1111 1111 0111 1111
# high 16b are undefined, and if you would clear eax, they would be 0 anyway
ret
照顾CF/OF会大大降低性能,并且仅在您确实必须验证计算的特殊情况下使用。大多数 SW 愉快地生活在 "garbage in, garbage out" 前提下(通常效果很好),并且只为 valid/expected 输入产生 valid/expected 输出。
进行所有计算 "safe/correct" 有点毫无意义(或者更确切地说 "expensive"),因为计算机的资源非常有限,而且无论如何您都无法使用计算机处理大数。本机 64b CPU 可以使用 64 或 128b 整数进行算术运算,除此之外,您可以使用可用内存大小的整数实现非本机操作,但迟早(可能非常晚,考虑到 32GiB 的操作内存)你会遇到点,在那里你会溢出足够大的整数(本例中为 28e32G)。自然地,你已经完成了 264/128/256+ 值甚至更快,这对于整数来说甚至不是那么多,尽管对于普通人来说甚至只是 232通常就够了。