From 05096a98a801f3c06041b1ab5fd41b976f09e59d Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 1 Oct 2017 18:57:45 +0000 Subject: [PATCH] compiler: correct semantics of floating point % operator (fix #830). --- .../compiler/transforms/llvm_ir_generator.py | 43 +++++++++++++++++-- artiq/test/lit/integration/arithmetics.py | 8 ++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 320131c96..a0f35b597 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -406,8 +406,7 @@ class LLVMIRGenerator: */ if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) { xmody += y; - --xdivy; - assert(xmody && ((y ^ xmody) >= 0)); + // ... } """ llx, lly = llfun.args @@ -423,7 +422,45 @@ class LLVMIRGenerator: llbuilder.ret(llbuilder.add(llxremy, lly)) llbuilder.ret(llxremy) 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): llfun = self.llmodule.get_global(name) diff --git a/artiq/test/lit/integration/arithmetics.py b/artiq/test/lit/integration/arithmetics.py index 1b167f379..01fd82efc 100644 --- a/artiq/test/lit/integration/arithmetics.py +++ b/artiq/test/lit/integration/arithmetics.py @@ -26,10 +26,10 @@ assert -1 % 8 == 7 #ARTIQ#assert int64(3) % -2 == -1 #ARTIQ#assert int64(-3) % -2 == -1 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.0 ** 2.0 == 9.0 assert 9.0 ** 0.5 == 3.0