浮点数不能全等比较吗php,汇编语言FCOM指令:比较浮点数值

浮点数不能使用 CMP 指令进行比较,因为后者是通过整数减法来执行比较的。取而代之,必须使用 FCOM 指令。

执行 FCOM 指令后,还需要采取特殊步骤,然后再使用逻辑 IF 语句中的条件跳转指令(JA、JB、JE 等)。由于所有的浮点数都为隐含的有符号数,因此,FCOM 执行的是有符号比较。

FCOM、FCOMP、FCOMPP

FCOM(比较浮点数)指令将其源操作数与 ST(0) 进行比较。源操作数可以为内存操作数或 FPU 寄存器。其语法如下表所示:

指令

说明

FCOM

比较 ST(0) 与 ST(1)

FCOM m32fp

比较 ST(0) 与 m32fp

FCOM m64fp

比较 ST(0) 与 m64fp

FCOM ST(i)

比较 ST(0) 与 ST(i)

FCOMP 指令的操作数类型和执行的操作与 FCOM 指令相同,但是它要将 ST(0) 弹岀堆栈。FCOMPP 指令与 FCOMP 相同,但是它有两次出栈操作。

条件码

FPU 条件码标识有 3 个,C3、C2 和 C0,用以说明浮点数比较的结果,如下表所示。由于 C3、C2 和 C0 的功能分别与零标志位 (ZF)、奇偶标志位 (PF) 和进位标志位 (CF) 相同,因此表中列标题给出了与之等价的 CPU 状态标识。

条件

C3(零标志位)

C2(奇偶标志位)

C0(进位标志位)

使用的条件跳转指令

ST(0) > SPC

0

0

0

JA.JNBE

ST(0) < SPC

0

0

1

JB.JNAE

ST(0) = SPC

1

0

0

JE.JZ

无序

1

1

1

(无)

提示:如果出现无效算术运算操作数异常(无效操作数),且该异常被屏蔽,则 C3、C2 和 C0 按照标记为“无序”的行来设置。

在比较了两个数值并设置了 FPU 条件码之后,遇到的主要挑战就是怎样根据条件分支到相应标号。这包括了两个步骤:

用 FNSTSW 指令把 FPU 状态字送入 AX。

用 SAHF 指令把 AH 复制到 EFLAGS 寄存器。

条件码送入 EFLAGS 之后,就可以根据 ZF、PF 和 CF 进行条件跳转。上表列出了每种标志位组合所对应的条件跳转。根据该表还可以推出其他跳转:如果 CF=0,则可以使用 JAE 指令引发控制转移;如果 CF=1 或 ZF=1,则可使用 JBE 指令引发控制转移;如果 ZF=0,则可使用 JNE 指令。

【示例】现有如下 C++ 代码段:

double X = 1.2;

double Y = 3.0;

int N = 0;

if( X < Y )

N = 1;

与之等效的汇编语言代码如下:

.data

X REAL8 1.2

Y REAL8 3.0

N DWORD 0

.code

if( X < Y )

; N = 1

fid X              ; ST(0) = X

fcomp Y        ;比较 ST (0)和 Y

fnstsw ax      ;状态字送入AX

sahf              ;AH 复制至!) EFLAGS

jnb L1           ;X不小于Y?跳过

mov Nz1       ; N = 1

L1:

P6 处理器的改进

对上面的例子需要说明一点的是浮点数比较的运行时开销大于整数比较。考虑到这一点,Intel P6 系列引入了 FCOMI 指令。该指令比较浮点数值,并直接设置 ZF、PF 和 CF。P6 系列以 Pentium Pro 和 Pentium II 处理器为起点。) FCOMI 的语法如下:

FCOMI 指令代替了之前代码段中的三条指令,但是增加了一条 FLD 指令。FCOMI 指令不使用内存操作数。

相等比较

几乎所有的编程入门教材都会警告读者不要进行浮点数相等的比较,其原因是在计算

过程中出现的舍入误差。现在通过计算表达式 (sqrt(2.0)*sqrt(2.0)) -2.0 来对这个问题进行说明。从数学上看,这个表达式应 该等于0,但计算结果却相差甚远(约等于 4.4408921E-016)。 使用如下数据,下表列出了每一步计算后FPU堆栈的情况:

vail REAL8 2.0

指令

FPU堆栈

fidvall

ST(0) : +2.0000000E+000

fsqrt

ST(0) : +1.4142135E+000

fmul

ST(0), ST(0)    ST(0) : +2.0000000E+000

fsub vail

ST(0) : +4.4408921E-016

比较两个浮点数 n 和 y 的正确方法是取它们差值的绝对值|x-y|,再将其与用户定义的误差值 epsilon 进行比较。汇编语言代码如下,其中,epsilon 为两数差值允许的最大值,不 大于该值则认为这两个浮点数相等:

.data

epsilon REAL8 1.0E-12

val2 REAL8 0.0                      ;比较的数值

val3 REAL8 1.01E —13          ;认为等于^&丄2

.code

;如果 (val2 == val3 ),显示"Values are equal".

fid epsilon

fid val2

fsu val3

fabs

fcomi ST(0)ZST(1)

ja skip

mWrite

skip:

下表跟踪程序执行过程,显示了前四条指令执行后的堆栈情况。

指令

FPU堆栈

指令

FPU堆栈

fid epsilon

ST(0): +1.0000000E-012

ST(1): +1.0000000E-012

fid val2

ST(0): +0.0000000E+000

fabs

ST(0): +1.0010000E-013

ST(1): +1.0000000E-012

ST(1): +1.0000000E-012

fsub val3

ST(0): -1.0010000E-013

fcomi ST(0), ST(1)

ST(0)

如果将 val3 重新定义为大于 epsilon,它就不会等于 val2:

val3 REAL8  1.001E-12    ;不相等

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值