为何idiv指令在RDX中生成的带符号除法余数结果不正确?
为什么
idiv计算-1 mod 20得到15而非预期的19? 你碰到的问题本质上有两个关键点:一是带符号除法中rdx的初始化方式错误,二是x86的idiv指令返回的余数规则和你预期的数学模运算规则有差异。
1. 错误的直接原因:rdx初始化错了
x86-64的idiv指令是把**128位的带符号数rdx:rax**当成被除数,除以你指定的源操作数(这里就是rbx里的20)。当你要计算64位带符号数-1除以20时,必须先把rax里的-1符号扩展到rdx,形成完整的128位被除数。
你用mov rdx, 0初始化rdx,相当于把被除数变成了0x0000000000000000FFFFFFFFFFFFFFFF——也就是十进制的4294967295,一个正数。用这个正数除以20:
- 商是
4294967295 ÷ 20 = 214748364 - 余数是
4294967295 - (20 × 214748364) = 15
这就是你在GDB里看到RBX=15的根本原因。
正确的初始化方式是用cqo指令,它会自动把rax里的64位带符号数符号扩展到rdx,比如rax=-1时,rdx会被设为0xffffffffffffffff(和rax的符号一致,都是-1的补码):
mov rax, -1 cqo ; 执行后rdx和rax都是-1的补码 mov rbx, 20 idiv rbx
2. 为什么正确初始化后还要调整余数?
就算你用cqo正确初始化了rdx,执行idiv后得到的余数也会是-1,而不是你想要的19——这是因为x86的idiv遵循两个规则:
- 余数的符号必须和被除数一致
- 商是向零取整的
对于-1 ÷ 20来说,向零取整的商是0,所以余数就是 -1 - (20 × 0) = -1。但数学上的模运算(-1 mod 20)要求结果是非负数,这时候你需要对余数做个小调整:如果余数是负数,就加上除数(因为除数是正数),就能得到正的模结果了:
mov rax, -1 mov rbx, 20 cqo idiv rbx test rdx, rdx ; 检查余数是否为负 jns store_result ; 余数非负的话直接保存 add rdx, rbx ; 余数为负就加上除数转成正数 store_result: mov rbx, rdx ; 此时RBX的值就是19,符合你的预期
内容的提问来源于stack exchange,提问作者hossein ahmadi
相关产品推荐
相关产品推荐

