Skip to content

Fix compiled SpEL > and >= comparisons with NaN - #37574

Open
CCCLL wants to merge 1 commit into
spring-projects:mainfrom
CCCLL:fix/spel-compiled-nan-comparison
Open

CCCLL wants to merge 1 commit into
spring-projects:mainfrom
CCCLL:fix/spel-compiled-nan-comparison

Conversation

@CCCLL

@CCCLL CCCLL commented Oct 23, 2026

Copy link
Copy Markdown

A compiled SpEL expression that compares double or float operands
with > or >= evaluates to true when one of the operands is NaN.
In interpreted mode, as in Java, the result is false. As a consequence,
the same expression changes its result once it gets compiled, for
example after about 100 evaluations in MIXED mode.

SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expression = (SpelExpression) parser.parseExpression("1.0d > T(Double).NaN");
expression.getValue();         // false
expression.compileExpression();
expression.getValue();         // true (false with this change)

Operator.generateComparisonCode() always emits DCMPG/FCMPG, which
push 1 if either operand is NaN, so the IFLE/IFLT jump used for
>/>= is never taken. javac uses DCMPL/FCMPL for > and >=,
and DCMPG/FCMPG for < and <=. This change does the same. < and
<= already returned false and keep using DCMPG/FCMPG.

The new parameterized test covers the four operators with NaN on
either side, for double and float. Without the change, the eight >
and >= cases fail once compiled; with the change, all 16 cases pass.
./gradlew :spring-expression:check passes on JDK 25.

The generated code has been like this since the SpEL compiler was
introduced, so 7.0.x is affected as well.

Prior to this commit, a compiled SpEL expression that compared double
or float operands with the > or >= operator evaluated to true if one of
the operands was NaN, whereas the same expression evaluated to false in
interpreted mode, as it does in Java. For example,
`1.0d > T(Double).NaN` was false when interpreted but true once
compiled.

The generated bytecode always used DCMPG/FCMPG, which push 1 if either
operand is NaN, so the subsequent IFLE/IFLT jump to the false branch was
never taken for > and >=.

This commit uses DCMPL/FCMPL for the > and >= operators, as javac does,
so that comparisons with NaN evaluate to false in compiled mode as well.

Signed-off-by: Jialin Chen <cccll2019@gmail.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 23, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants