Repository navigation
fix(core): keep NaN semantics of negated float and double comparisons - #3048
Merged
Merged
Conversation
`cmpg`/`cmpl` return 1/-1 for NaN arguments, so `cmp(a, b) op 0` was folded into `a op b` even where that gives a different result for NaN, and the later condition inversions then flipped `a < b` into `a >= b`. For example `if (!(a < b))` was decompiled as `if (a >= b)`. Keep float and double `cmp` in the condition until code generation, so inversions stay exact, and generate `a op b` or `!(a inverted_op b)` depending on which one keeps the result for NaN. Co-Authored-By: Claude Opus 5.5 <[email protected]>
skylot
approved these changes
Nov 2, 2026
skylot
left a comment
Owner
There was a problem hiding this comment.
Looks good.
Although I prefer to apply changes only in visitors and keep codegen as simple as possible, but for this case I am not sure how to better implement that, so it is fine for now 🤣
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
❗ Please review the guidelines for contributing
Description
Negated float and double comparisons are decompiled into the opposite comparison, which gives a different result for NaN:
This is decompiled as
if (a >= b) { return 1; }, sonotLess(Double.NaN, 1.0)returns 2 instead of 1. The same happens withreturn !(a > b);(decompiled asreturn a <= b;) and with ternaries.cmpg-*/cmpl-*return 1 or -1 when an argument is NaN, socmp(a, b) op 0equalsa op bonly for some ops. Also, once the condition is folded intoa op b, the condition inversions that run later (IfOp.invert(),invertIfRegion) are no longer exact.With this change, a float/double
cmpstays wrapped in the condition until code generation, so every inversion still works on the integercmpresult.ConditionGenthen writesa op bif that gives the same result for NaN, and!(a inverted_op b)otherwise. Long comparisons are folded as before. Comparisons that were already correct, like the ones inTestCmpOp/TestCmpOp2, decompile to the same code as before.TestFloatCompareNaNis new. On master, it fails in both thed8-j11andjava-11profiles (expected: 1 but was: 2from the auto check). With the fix,./gradlew test,spotlessCheckandcheckstylepass.I used Claude Code to write this change.