所以我理解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余数。
答案 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所说的应该做的事情。 (另请参阅x86标签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后备。如果您知道范围有限,那么转换为/从整数转换可能会有效。