From db40730ff05ea6cd491db3cbce7d6fccc61e7c45 Mon Sep 17 00:00:00 2001 From: Sabith Saheb Date: Wed, 14 Oct 2026 12:40:02 +0530 Subject: [PATCH] Fix != to return true when either operand is NaN XPath 1.0 section 3.4 compares numbers per IEEE 754, where NaN is not equal to anything, including itself: '=' with a NaN operand is false and '!=' is true. CoreOperationCompare.equal returned false early for NaN before the 'result ^ invert' step that implements '!=', so both operators answered false. Java's == on doubles already follows IEEE 754, so the early returns are removed and the comparison result flows through the inversion. Completes JXPATH-95. --- .../jxpath/ri/compiler/CoreOperationCompare.java | 13 +++---------- .../jxpath/ri/compiler/CoreOperationTest.java | 9 ++++++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java b/src/main/java/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java index ba0b9ea2e..0248c38d4 100644 --- a/src/main/java/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java +++ b/src/main/java/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java @@ -137,16 +137,9 @@ protected boolean equal(Object l, Object r) { if (l instanceof Boolean || r instanceof Boolean) { result = l == r || InfoSetUtil.booleanValue(l) == InfoSetUtil.booleanValue(r); } else if (l instanceof Number || r instanceof Number) { - // if either side is NaN, no comparison returns true: - final double ld = InfoSetUtil.doubleValue(l); - if (Double.isNaN(ld)) { - return false; - } - final double rd = InfoSetUtil.doubleValue(r); - if (Double.isNaN(rd)) { - return false; - } - result = ld == rd; + // Numbers are compared per IEEE 754 (XPath 1.0 section 3.4): NaN is not + // equal to anything, including itself, so "=" is false and "!=" is true. + result = InfoSetUtil.doubleValue(l) == InfoSetUtil.doubleValue(r); } else { if (l instanceof String || r instanceof String) { l = InfoSetUtil.stringValue(l); diff --git a/src/test/java/org/apache/commons/jxpath/ri/compiler/CoreOperationTest.java b/src/test/java/org/apache/commons/jxpath/ri/compiler/CoreOperationTest.java index 7c47790b3..7e960ca87 100644 --- a/src/test/java/org/apache/commons/jxpath/ri/compiler/CoreOperationTest.java +++ b/src/test/java/org/apache/commons/jxpath/ri/compiler/CoreOperationTest.java @@ -105,21 +105,24 @@ void testNan() { assertXPathValue(context, "$nan <= $nan", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan >= $nan and $nan <= $nan", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan = $nan", Boolean.FALSE, Boolean.class); - assertXPathValue(context, "$nan != $nan", Boolean.FALSE, Boolean.class); + assertXPathValue(context, "$nan != $nan", Boolean.TRUE, Boolean.class); assertXPathValue(context, "$nan > 0", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan < 0", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan >= 0", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan <= 0", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan >= 0 and $nan <= 0", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan = 0", Boolean.FALSE, Boolean.class); - assertXPathValue(context, "$nan != 0", Boolean.FALSE, Boolean.class); + assertXPathValue(context, "$nan != 0", Boolean.TRUE, Boolean.class); assertXPathValue(context, "$nan > 1", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan < 1", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan >= 1", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan <= 1", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan >= 1 and $nan <= 1", Boolean.FALSE, Boolean.class); assertXPathValue(context, "$nan = 1", Boolean.FALSE, Boolean.class); - assertXPathValue(context, "$nan != 1", Boolean.FALSE, Boolean.class); + assertXPathValue(context, "$nan != 1", Boolean.TRUE, Boolean.class); + assertXPathValue(context, "number('foo') = 1", Boolean.FALSE, Boolean.class); + assertXPathValue(context, "number('foo') != 1", Boolean.TRUE, Boolean.class); + assertXPathValue(context, "number('foo') != number('foo')", Boolean.TRUE, Boolean.class); } @Test