New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fp.min/fp.max does not work correctly for -0/+0 #68
Comments
This one's actually quite tricky, because there is no clear definition of the semantics of min/max for zeros. The CPUs I tested the implementation against implements min(+0, -0) = +0 and min(-0, +0) = -0, i.e., the return value is the first argument. Neither the IEEE standard, nor the SMT theory definition gives a clear answer, but I think min(-0, +0) = min(+0, -0) = +0 will ultimately be the conclusion, so as to stay in line with a couple of other upcoming changes/clarifications to the FP theory, so I think I can make those changes now. |
Fixes Z3Prover#68 Signed-off-by: Christoph M. Wintersteiger <cwinter@microsoft.com>
Closing as fixed, feel free to reopen if there are further problems. |
Thanks for the fixes @wintersteiger; I'll test them later today. Did you have a typo in your comment? You said: "but I think min(-0, +0) = min(+0, -0) = +0" You either meant max, or the result to be -0, I'm guessing? In any case, I think the SMTLib is quite clear on min/max. The theory document is rather sparse on it, but the "reference" paper at http://www.philipp.ruemmer.org/publications/smt-fpa.pdf
I do agree, however, that the IEEE-spec itself and fmin/fmax in the C-library seem to be returning the second argument actually. (You said the "first" argument.) I tested with the following program:
|
No, that was not a typo, min(-0,+0) == +0, with the justification that -0 is not smaller than +0 after conversion to reals. I had the same intuition about this as you, and implemented min/max as well as Real and FP conversions to floating-point numbers the same way, e.g.., when converting Real numbers to FP numbers and rounding towards negative, I would select -0 instead of +0, but I was outvoted and it will now always be +0. The document you cite is a very old document and there were numerous changes to the theory since then. The current theory definition is not that clear anymore. I hear that there is a paper in the works which will assign precise semantics, but that is still in the works (and I'm not involved). Yes, it could well have been the second argument instead of the first one. Note that your demo program does not select a particular floating-point unit, so depending on the system that you compile this on, it will either use the x87 (when in 32-bit mode) or the SSE2 unit (when in 64 bit mode), and they do not agree on some results (and I think min/max was such a discrepancy, but I can't remember exactly). On Windows, by default, it may decide to further approximate unless /fp:precise is added. |
Ah good point. Indeed http://smt-lib.org/papers/BTRW14.pdf specifically says that min/max are underspecified for +0/-0:
So, just to clarify what you've implemented and for the record, can you tell me what Z3 will produce for the following calls:
We can then refer to this ticket later on in case of confusion again. |
In the current implementation (as of earlier today, unstable, see commit above), they all return +0. |
Thank you! Let me just point out that this is in "disagreement" with the current Intel PRM for MAXPS, MAXPD, MINPS, MINPD and other floating-point instructions; which all return the "second" argument for these cases. This creates a sticking point that one has to watch out for. I wish the IEEE754 were just clear on this, alas that ship has sailed long time ago. |
Indeed, I feel much the same about this. This would have been trivial to sort out at the time IEEE754 was compiled, now it's a bit of a pain. There aren't too many operations that suffer from this problem though. Add/sub/muk/div are almost unaffected by this (except when rounding to negative). So, we can always wrap our min/max's in if-then-elses that check for double zeros and return our preferred result, otherwise do the default. This way all different implementations can be handled soundly and it's not more expensive, as internally we'd do exactly the same. |
@wintersteiger I was surprised to see the problem remained after I did a pull; but then noticed that you fixed it in your own branch.. Would it be possible to pull the fix in to z3/unstable? |
Indeed, I just forgot to push that, it's there now. |
Fixes the fix for Z3Prover#68
@wintersteiger It appears you've changed the code for this and now +0/-0 cases actually return the second argument? In an earlier comment, you said '+0' was the chosen result in the mixed-zero cases, but it does appear you're now just returning the second argument. Wanted to double-check on that. |
Hmm, apparently I changed the code to return the second argument, but in my comments I said all cases would be +0.0. I probably mixed up this one with an fp.rem bug report at the time. In any case, I've added yet more special case handling, so this should really be +0.0 everywhere now. |
…ument. Another piece of fix Z3Prover#68
This has come up during differential testing. Consider the following input:
Z3 reports Based on the comment from @LeventErkok above with the snippet from the standard, I'm leaning towards Z3 being incorrect here. From the standard, it's clear that -0.0 and +0.0 are possible results for the |
No it's not, that's exactly the point, regardless of which of the 3 involved standards you refer to. Part of the SMT FP standard is to avoid as many unspecified results as possible - those create big problems for solver developers and only tiny benefits for the users. Unspecifiedness can always be recovered on the application level by guarding unspecified functions, e.g., fp.min, with an if-then-else that catches those special cases, so when we push that onto the end-user, they are responsible for picking a solver that supports uninterpreted functions; if for some reason they can't choose such a solver, then they can't have general unspecifiedness. I had brought the min/max +-0.0 case up with them before, but so far they haven't delivered a clear solution yet, and they may well have forgotten about it. Before the current SMT FP standard was released we used a google group to discuss all these issues, it's been quiet recently but I think it's the best place to bring this one up again, The group is called smt-fp and it might require getting permissions from the owners (should be quick though). I've taken a quick glance at BTRW14 again, and it seems like they did update the min/max paragraph to what appears like unspecifiedness, I'll confirm with them that this is indeed their intent. |
Update: There is now also BTRW15. Not sure that's the latest version though. |
Meanwhile I received confirmation, those disputed cases will indeed stay unspecified alongside the conversions to other theories, so that's what I'll implement now. |
Partially addresses Z3Prover#68.
Partially addresses Z3Prover#68.
…v_converter and in theory_fpa. Fixes Z3Prover#68
fp.min/fp.max should now behave as defined in BTRW15. I'd be grateful if you could run your own tests and then report any new problems back to us. |
Just to be absolutely sure I'm understanding what the semantics are, if we have the same test case from before (duplicated below for convenience):
Then this should be |
Yes to both, the test case is sat and it's non-deterministic. |
@wintersteiger All my tests confirm the nondeterministic behavior as you predicted. As unfortunate as that is, I do agree that this is the behavior required by the standard. |
* local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Signed-off-by: Lev Nachmanson <levnach@hotmail.com> add explanations to hnf cuts Signed-off-by: Lev Nachmanson <levnach@hotmail.com> nits and virtual methods (Z3Prover#68) * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> cleanup from std::cout Signed-off-by: Lev Nachmanson <levnach@hotmail.com> handle the case when the number of terms is greater than the number of variables in hnf Signed-off-by: Lev Nachmanson <levnach@hotmail.com> method name's fix Signed-off-by: Lev Nachmanson <levnach@hotmail.com> restore hnf_cutter to work with m_row_count <= m_column_count Signed-off-by: Lev Nachmanson <levnach@hotmail.com> tune addition of rational numbers (Z3Prover#70) * log quantifiers only if present Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge and fix some warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * set new arith as default for LIA Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * prepare for mixed integer-real Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix default tactic usage Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> give shorter explanations, call hnf only when have a not integral var Signed-off-by: Lev Nachmanson <levnach@hotmail.com> overhaul of mpq (Z3Prover#71) * log quantifiers only if present Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge and fix some warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * set new arith as default for LIA Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * prepare for mixed integer-real Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix default tactic usage Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * overhaul of mpz/mpq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disabled temporary setting Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove prints Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> fix for 32 bit build (Z3Prover#72) * log quantifiers only if present Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge and fix some warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * set new arith as default for LIA Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * prepare for mixed integer-real Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix default tactic usage Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * overhaul of mpz/mpq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disabled temporary setting Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove prints Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * customize for 64 bit Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> yes (Z3Prover#74) * log quantifiers only if present Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge and fix some warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * set new arith as default for LIA Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * prepare for mixed integer-real Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix default tactic usage Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * overhaul of mpz/mpq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disabled temporary setting Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove prints Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * customize for 64 bit Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * customize for 64 bit Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * more refactor Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> fix the merge Signed-off-by: Lev Nachmanson <levnach@hotmail.com> fixes in maximize_term untested Signed-off-by: Lev Nachmanson <levnach@hotmail.com> fix compilation (Z3Prover#75) * log quantifiers only if present Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge and fix some warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * set new arith as default for LIA Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * local Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * virtual method in bound propagator Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * prepare for mixed integer-real Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix default tactic usage Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * overhaul of mpz/mpq Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disabled temporary setting Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove prints Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * customize for 64 bit Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * customize for 64 bit Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * more refactor Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * relax check Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * change for gcc Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * merge Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
For inputs
+0/-0
,fp.min
andfp.max
seem to be doing the "wrong" thing currently.For the following benchmark:
Z3 responds:
Which suggests
+0, -0, +0
are solutions; but the result should actually be-0, -0, +0
A parallel benchmark for fp.max exhibits the same problem:
Z3 responds:
Which suggests the solution is
-0, +0, -0
; which should actually be+0, +0, -0
.The text was updated successfully, but these errors were encountered: