Nasm - 将 2 个值相乘时出现奇怪的结果
Nasm - weird results when multiplying 2 values
编辑:我正在使用 this 计算器来检查结果,但似乎计算器本身正在计算错误的值,从而导致了这个问题。
在使用 mul
指令时,我注意到将 2 个高数相乘会得到错误的值。示例:
mov eax, 0ffffffffh
mov ebx, 0ffffffffh
mul ebx
; eax:00000001 I expected eax to be 0
; edx:FFFFFFFE
mov eax, 0ffffff00h
mov ebx, 0ffffffffh
mul ebx
; eax:00000100 I expected eax also to be 0
; edx:FFFFFEFF
我知道-1 * -1
是1
,ffffffffh
是负一。但是用无符号乘法将这两个数字相乘不会得到 1
,是吗?另一个例子也是如此。
另外,当使用 imul
指令时,我也得到了奇怪的数字,但我认为这是因为有符号的乘法:
mov eax, 0fffffff0h
mov ebx, 5
imul ebx
; eax:FFFFFFB0
; edx:FFFFFFFF ; I expected edx to be 4
你的结果是正常的,你的期望是错误的。 IDK 你错误的期望来自哪里,但是 32 位 mul
的 64 位结果进入 EDX:EAX.
结果是来自一个在线计算器,大概使用了 Javascript 数字,即具有 53 位尾数的 double
精度浮点数,它将 0xfffffffe00000001
舍入到最近的可表示双精度,即 0xfffffffe00000000
.
对于带符号的情况,您完全错误地使用了计算器,期望它将您的输入解释为 32 位 2 的补码。
乘法结果的低半部分不取决于您是否将输入解释为带符号的,所以是的,作为快捷方式,我们可以使用 -1 * -1 = 1
在此处获得低半部分。 (这就是为什么英特尔只为 imul
添加有效的非扩展形式,例如 imul eax, edx, -1
)。
如果我们简单地在像 calc
这样的扩展精度计算器中尝试一下(在 Ubuntu 中打包为 apcalc
,在 Arch 中打包为 calc
):
; base2(16) // ask for hex output as well as decimal
; 0x0ffffffff ^ 2
18446744065119617025 /* 0xfffffffe00000001 */
; 0x0ffffff00 * 0x0ffffffff
18446742969902956800 /* 0xfffffeff00000100 */
所以这证实了 CPU 的结果。请记住,奇数乘以奇数是奇数,因此您对 0xffffffff * 0xffffffff
的 EAX=0 猜测也可以这样排除。
对于带符号的,使用任意精度计算器有点棘手:
; (0xfffffff0 - 2^32) * 5
-80 /* -0x50 */
; . + 2^64 // get the 64-bit 2's complement bit-pattern for that negative number
18446744073709551536 /* 0xffffffffffffffb0 */
那个小的无符号数当然分为 0xffffffff
和 0xffffffb0
,EDX 只是全一,与 EAX 的高位相同,因为这就是符号扩展的工作原理。
编辑:我正在使用 this 计算器来检查结果,但似乎计算器本身正在计算错误的值,从而导致了这个问题。
在使用 mul
指令时,我注意到将 2 个高数相乘会得到错误的值。示例:
mov eax, 0ffffffffh
mov ebx, 0ffffffffh
mul ebx
; eax:00000001 I expected eax to be 0
; edx:FFFFFFFE
mov eax, 0ffffff00h
mov ebx, 0ffffffffh
mul ebx
; eax:00000100 I expected eax also to be 0
; edx:FFFFFEFF
我知道-1 * -1
是1
,ffffffffh
是负一。但是用无符号乘法将这两个数字相乘不会得到 1
,是吗?另一个例子也是如此。
另外,当使用 imul
指令时,我也得到了奇怪的数字,但我认为这是因为有符号的乘法:
mov eax, 0fffffff0h
mov ebx, 5
imul ebx
; eax:FFFFFFB0
; edx:FFFFFFFF ; I expected edx to be 4
你的结果是正常的,你的期望是错误的。 IDK 你错误的期望来自哪里,但是 32 位 mul
的 64 位结果进入 EDX:EAX.
结果是来自一个在线计算器,大概使用了 Javascript 数字,即具有 53 位尾数的 double
精度浮点数,它将 0xfffffffe00000001
舍入到最近的可表示双精度,即 0xfffffffe00000000
.
对于带符号的情况,您完全错误地使用了计算器,期望它将您的输入解释为 32 位 2 的补码。
乘法结果的低半部分不取决于您是否将输入解释为带符号的,所以是的,作为快捷方式,我们可以使用 -1 * -1 = 1
在此处获得低半部分。 (这就是为什么英特尔只为 imul
添加有效的非扩展形式,例如 imul eax, edx, -1
)。
如果我们简单地在像 calc
这样的扩展精度计算器中尝试一下(在 Ubuntu 中打包为 apcalc
,在 Arch 中打包为 calc
):
; base2(16) // ask for hex output as well as decimal
; 0x0ffffffff ^ 2
18446744065119617025 /* 0xfffffffe00000001 */
; 0x0ffffff00 * 0x0ffffffff
18446742969902956800 /* 0xfffffeff00000100 */
所以这证实了 CPU 的结果。请记住,奇数乘以奇数是奇数,因此您对 0xffffffff * 0xffffffff
的 EAX=0 猜测也可以这样排除。
对于带符号的,使用任意精度计算器有点棘手:
; (0xfffffff0 - 2^32) * 5
-80 /* -0x50 */
; . + 2^64 // get the 64-bit 2's complement bit-pattern for that negative number
18446744073709551536 /* 0xffffffffffffffb0 */
那个小的无符号数当然分为 0xffffffff
和 0xffffffb0
,EDX 只是全一,与 EAX 的高位相同,因为这就是符号扩展的工作原理。