NASM x64中COMISD搭配JNL指令失效问题咨询
浮点数比较中JNL/JNG指令异常的原因分析
在NASM x64环境下,使用COMISD指令比较浮点数时,整数比较指令JNL无法正常工作;替换为JNG后又出现逻辑异常,以下是对应的代码示例:
对应代码
C代码
float total = 7.3; float media = 2.2; main() { if(!total < media){ write(total); } }
汇编代码
extern printf extern scanf section .data fmt_f: db "%.2f", 0 total: dq 7.30 media: dq 2.20 section .text global main main: mov qword [total], 0 ; (!total) movsd xmm0, [total] movsd xmm1, [media] comisd xmm0, xmm1 jnl label0 sub rsp, 8 movsd xmm0, qword [total] mov rdi,fmt_f mov eax, 1 call printf add rsp, 8 label0: mov rax,0 ret
异常原因分析
1. JNL无法工作的本质
JNL(不小于则跳转)是整数专用跳转指令,它的判断逻辑依赖整数比较产生的SF(符号位)和OF(溢出位)标志位,条件为SF == OF时触发跳转。
但COMISD比较浮点数时,只会设置CF(进位位)、ZF(零位)、PF(奇偶位)三个标志位,完全不涉及SF和OF。JNL读取的标志位和COMISD的比较结果毫无关联,自然无法正确判断浮点数的大小关系。
2. 替换JNG后逻辑异常的原因
JNG(不大于则跳转)同样是整数指令,判断条件为(SF ^ OF) | ZF。由于COMISD不会修改SF和OF,这两个标志位的值是之前指令遗留的随机值,导致JNG的判断完全脱离浮点数比较的实际结果:
- 当
!total < media(0.0 < 2.2)时,COMISD设置CF=1、ZF=0,但SF和OF的随机组合可能让(SF^OF)|ZF为假,不跳转从而执行printf; - 当
total < media(7.3 < 2.2)时,COMISD设置CF=0、ZF=0,此时SF和OF的随机值也可能让(SF^OF)|ZF为假,同样不跳转执行printf。
最终出现两种条件都触发printf的异常。
正确做法
浮点数比较后必须使用对应CF/ZF的跳转指令,匹配COMISD的标志位规则:
xmm0 == xmm1:ZF=1,对应跳转指令JE/JZxmm0 < xmm1:CF=1,对应跳转指令JB/JCxmm0 > xmm1:CF=0且ZF=0,对应跳转指令JAxmm0 >= xmm1:CF=0,对应跳转指令JAE/JNCxmm0 <= xmm1:CF=1或ZF=1,对应跳转指令JBE
比如原C代码逻辑if((!total) < media),对应的汇编应该在COMISD后使用JAE label0(若大于等于则跳转到label0,否则执行printf),这样就能正确匹配浮点数比较结果。
内容的提问来源于stack exchange,提问作者GreatField
相关产品推荐
相关产品推荐

