Assembly x86_64程序集双重比较有时无法按预期工作

Assembly x86_64程序集双重比较有时无法按预期工作,assembly,floating-point,double,x86-64,Assembly,Floating Point,Double,X86 64,我知道浮点不准确,但我需要一种解决方法 所以,我需要做一个图遍历,我把所有的节点放到堆栈中,给所有的邻居添加路由,然后给所有的非邻居添加一个大路由,最后遍历并找到最好的路由。它适用于从邻居创建的路由,但在使用大量数据创建的路由上会中断 下面是代码的相关部分 此处指定了大路线 movsd xmm0, [big] mov [r11+rax+TAR], r9 movsd [r11+rax+DIS], xmm0 mov [r11+rax+HOP], r9 这里进行了比较,以查看是否找到了更好的路线 u

我知道浮点不准确,但我需要一种解决方法

所以,我需要做一个图遍历,我把所有的节点放到堆栈中,给所有的邻居添加路由,然后给所有的非邻居添加一个大路由,最后遍历并找到最好的路由。它适用于从邻居创建的路由,但在使用大量数据创建的路由上会中断

下面是代码的相关部分

此处指定了大路线

movsd xmm0, [big]
mov [r11+rax+TAR], r9
movsd [r11+rax+DIS], xmm0
mov [r11+rax+HOP], r9
这里进行了比较,以查看是否找到了更好的路线

ucomisd xmm0, xmm2
ja  bMBD
jmp eMBD
bMBD:
    mov rax, [r8+ROT]
    mov [rax+r13+HOP], r11
    movsd [rax+r13+DIS], xmm2
eMBD:
上面的代码块就是问题所在,下面是我相信代码正在做的工作的分解

D要求D提供更好的路线,但什么也没发生:好

D问A,找到一个,计算距离为3.3+5.4=8.7,它更好,并替换它的当前路线:好

D问B,找到一个距离为8.7+0=8.7的。出于某种原因,这被认为是更好的,它的路线被取代:坏

那么,为什么
ja
条件返回8.7>8.7,但仅在距离初始化为较大数值的路线上返回(我已尝试减小数值大小,但结果相同)


谢谢。

在浮点数学中,舍入差异是一个事实。这是可以解释的

如果您只需要找到任何一条最佳路线,那么我认为
3.3+5.4>8.7+0.0
中没有问题;任何一条路线都可以

如果您需要检索共享相同最佳距离的所有路线,那么我同意您确实需要
3.3+5.4
等于
8.7+0.0
。 有几种方法可以做到这一点;见下文。对于示例代码,我将使用伪代码而不是汇编代码,因为这个问题并不特定于任何语言

1.忽略细微差别 如果差值的绝对值小于某个小数值(如0.001),则认为数值相等。 这意味着比较逻辑变得稍微复杂一些。 而不是:

if x < y then
    return LESS_THAN
elseif x > y then
    return GREATER_THAN
else
    return EQUAL
routeA = 3.3 + 5.4
routeB = 8.7 + 0.0
if routeA > routeB then ...
你会做:

epsilon = 0.001
if x < y - epsilon then
    return LESS_THAN
elseif x > y + epsilon then
    return GREATER_THAN
else
    return EQUAL
routeA = 33 + 54
routeB = 87 + 0
if routeA > routeB then ...

舍入差异是浮点数学中的一个事实。这是可以解释的

如果您只需要找到任何一条最佳路线,那么我认为
3.3+5.4>8.7+0.0
中没有问题;任何一条路线都可以

如果您需要检索共享相同最佳距离的所有路线,那么我同意您确实需要
3.3+5.4
等于
8.7+0.0
。 有几种方法可以做到这一点;见下文。对于示例代码,我将使用伪代码而不是汇编代码,因为这个问题并不特定于任何语言

1.忽略细微差别 如果差值的绝对值小于某个小数值(如0.001),则认为数值相等。 这意味着比较逻辑变得稍微复杂一些。 而不是:

if x < y then
    return LESS_THAN
elseif x > y then
    return GREATER_THAN
else
    return EQUAL
routeA = 3.3 + 5.4
routeB = 8.7 + 0.0
if routeA > routeB then ...
你会做:

epsilon = 0.001
if x < y - epsilon then
    return LESS_THAN
elseif x > y + epsilon then
    return GREATER_THAN
else
    return EQUAL
routeA = 33 + 54
routeB = 87 + 0
if routeA > routeB then ...

ucomisd
指令后的调试器输出,显示
xmm0
xmm2
eflags
。如果这是手动调整的asm,请注意索引寻址模式,如
[r11+rax+TAR]
。因此,您实际上可以通过使用ALU指令计算寄存器中的地址来保存一个融合域uop,这样所有三个存储都可以微融合。如果您没有备用寄存器,或者ALU UOP是一个瓶颈,但前端(总融合域UOP)没有,那么您不应该更改任何内容。在
ucomisd
指令后的调试器输出,显示
xmm0
xmm2
eflags
。如果是手动调整的asm,注意索引寻址模式,如
[r11+rax+TAR]
。因此,您实际上可以通过使用ALU指令计算寄存器中的地址来保存一个融合域uop,这样所有三个存储都可以微融合。如果您没有备用寄存器,或者ALU UOP是一个瓶颈,但前端(总融合域UOP)没有,那么您不应该更改任何内容。