fprem1没有吐出我预期的结果

时间:2016-07-30 05:39:21

标签: assembly

所以我理解fprem1执行ST0 / ST1并将余数放在ST0上(因此替换它)

但是当我做这样的事情时

mov dword ptr [402000],2
mov dword ptr [402004],3
fild dword ptr [402000]
fild dword ptr [402004]
freem1 

当我期待它应该是1时,我得到-1余数。

1 个答案:

答案 0 :(得分:2)

TL:DR:使用fprem而非fprem1来获取您期望的行为。或者更好的是,使用SSE2而不是使用过时的x87。

fprem实现了fmod() IEEE / ISO C standard function,而fprem1实现了the remainder() standard function

fprem1正在完成the instruction reference manual entry for it所说的应该做的事情。 (另请参阅标签wiki以获取英特尔官方PDF的链接。简明语:

  

余数代表以下值:

     

剩余←ST(0) - (Q * ST(1))

     

这里,Q是一个整数值,它是通过舍入 [ST(0)/ ST(1)] 的浮点数商得到的,最接近整数值。剩余量的大小小于或等于模量大小的一半(即ST(1))

结果表还证实,两个正输入(+ F)可以给出正或负结果,或者为正零。 (+/- F或+0)。

inputs: st0=3  st1=2
3/2 = 1.5  
Round to nearest(1.5): Q = 2.0  
Remainder = 3 - 2 * 2 = -1

您希望它像整数模运算符一样工作,其中除法结果被截断为零,而不是舍入到最接近。 That's what fprem does,而非fprem1

  

fprem(不是fprem1):
  ......剩余部分的符号与股息的符号相同。

另请注意,x87已过时,在新代码中,通常最好使用SSE2。 e.g。

mov       eax, 2
cvtsi2sd  xmm2, eax
mov       eax, 3
cvtsi2sd  xmm3, eax
 ; or just accept them as function args in registers

; x=2 in xmm2.   y=3 in xmm3
movaps    xmm0, xmm3    ; save a copy of y
divsd     xmm3, xmm2    ; y/x = 3/2 = 1.5
roundsd   xmm1, xmm3, 0 ; SSE4.1  round to nearest integer.
mulsd     xmm1, xmm2    ; Q * divisor
subsd     xmm0, xmm1    ; dividend - (Q * divisor)

; xmm0 = y mod x   (fprem style, not fprem1)
; xmm3 = y/x

我忘记了当SSE4.1 roundsd不可用时gcc对nearbyint(x)所做的事情,但请检查(使用-ffast-math)SSE2后备。如果您知道范围有限,那么转换为/从整数转换可能会有效。