compiler: correct semantics of floating point % operator (fix #830).

This commit is contained in:
whitequark 2017-10-01 18:57:45 +00:00
parent 62f2693e36
commit 491c7ef898
2 changed files with 44 additions and 7 deletions

View File

@ -400,8 +400,7 @@ class LLVMIRGenerator:
*/ */
if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) { if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) {
xmody += y; xmody += y;
--xdivy; // ...
assert(xmody && ((y ^ xmody) >= 0));
} }
""" """
llx, lly = llfun.args llx, lly = llfun.args
@ -417,7 +416,45 @@ class LLVMIRGenerator:
llbuilder.ret(llbuilder.add(llxremy, lly)) llbuilder.ret(llbuilder.add(llxremy, lly))
llbuilder.ret(llxremy) llbuilder.ret(llxremy)
elif name == "__py_moddf4": elif name == "__py_moddf4":
assert False """
Reference Objects/floatobject.c
mod = fmod(vx, wx);
/* fmod is typically exact, so vx-mod is *mathematically* an
exact multiple of wx. But this is fp arithmetic, and fp
vx - mod is an approximation; the result is that div may
not be an exact integral value after the division, although
it will always be very close to one.
*/
// ...
if (mod) {
/* ensure the remainder has the same sign as the denominator */
if ((wx < 0) != (mod < 0)) {
mod += wx;
// ...
}
}
else {
/* the remainder is zero, and in the presence of signed zeroes
fmod returns different results across platforms; ensure
it has the same sign as the denominator; we'd like to do
"mod = wx * 0.0", but that may get optimized away */
mod *= mod; /* hide "mod = +0" from optimizer */
if (wx < 0.0)
mod = -mod;
}
"""
llv, llw = llfun.args
llrem = llbuilder.frem(llv, llw)
llremnonzero = llbuilder.fcmp_unordered('!=', llrem, ll.Constant(lldouble, 0.0))
llwltzero = llbuilder.fcmp_ordered('<', llw, ll.Constant(lldouble, 0.0))
llremltzero = llbuilder.fcmp_ordered('<', llrem, ll.Constant(lldouble, 0.0))
lldiffsign = llbuilder.icmp_unsigned('!=', llwltzero, llremltzero)
llcond = llbuilder.and_(llremnonzero, lldiffsign)
with llbuilder.if_then(llcond):
llbuilder.ret(llbuilder.fadd(llrem, llw))
llbuilder.ret(llrem)
def get_function(self, typ, name): def get_function(self, typ, name):
llfun = self.llmodule.get_global(name) llfun = self.llmodule.get_global(name)

View File

@ -26,10 +26,10 @@ assert -1 % 8 == 7
#ARTIQ#assert int64(3) % -2 == -1 #ARTIQ#assert int64(3) % -2 == -1
#ARTIQ#assert int64(-3) % -2 == -1 #ARTIQ#assert int64(-3) % -2 == -1
assert -1 % 8 == 7 assert -1 % 8 == 7
# assert 3.0 % 2.0 == 1.0 assert 3.0 % 2.0 == 1.0
# assert -3.0 % 2.0 == 1.0 assert -3.0 % 2.0 == 1.0
# assert 3.0 % -2.0 == -1.0 assert 3.0 % -2.0 == -1.0
# assert -3.0 % -2.0 == -1.0 assert -3.0 % -2.0 == -1.0
assert 3 ** 2 == 9 assert 3 ** 2 == 9
assert 3.0 ** 2.0 == 9.0 assert 3.0 ** 2.0 == 9.0
assert 9.0 ** 0.5 == 3.0 assert 9.0 ** 0.5 == 3.0