diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
index 50f454063c..ccbed69283 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
@@ -12,6 +12,7 @@
import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
import com.fasterxml.jackson.core.exc.InputCoercionException;
+import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.JacksonFeatureSet;
import com.fasterxml.jackson.core.util.RequestPayload;
@@ -1397,16 +1398,21 @@ public int currentTokenId() {
public boolean isExpectedNumberIntToken() { return currentToken() == JsonToken.VALUE_NUMBER_INT; }
/**
- * Access for checking whether current token is a numeric value token, but
- * one that is of "not-a-number" (NaN) variety (including both "NaN" AND
- * positive/negative infinity!): not supported by all formats,
- * but often supported for {@link JsonToken#VALUE_NUMBER_FLOAT}.
- * NOTE: roughly equivalent to calling !Double.isFinite()
- * on value you would get from calling {@link #getDoubleValue()}.
- *
- * @return {@code True} if the current token is of type {@link JsonToken#VALUE_NUMBER_FLOAT}
- * but represents a "Not a Number"; {@code false} for other tokens and regular
- * floating-point numbers
+ * Access for checking whether current token is a special
+ * "not-a-number" (NaN) token (including both "NaN" AND
+ * positive/negative infinity!). These values are not supported by all formats:
+ * JSON, for example, only supports them if
+ * {@link JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS} is enabled.
+ *
+ * NOTE: in case where numeric value is outside range of requested type -- + * most notably {@link java.lang.Float} or {@link java.lang.Double} -- and + * decoding results effectively in a NaN value, this method DOES NOT return + * {@code true}: only explicit incoming markers do. + * This is because value could still be accessed as a valid {@link BigDecimal}. + * + * @return {@code True} if the current token is reported as {@link JsonToken#VALUE_NUMBER_FLOAT} + * and represents a "Not a Number" value; {@code false} for other tokens and regular + * floating-point numbers. * * @throws IOException for low-level read issues, or * {@link JsonParseException} for decoding problems diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index 8188d31fb8..db6b15fb10 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -217,6 +217,21 @@ public abstract class ParserBase extends ParserMinimalBase */ protected String _numberString; + /** + * Marker for explicit "Not a Number" (NaN) values that may be read + * by some formats: this includes positive and negative infinity, + * as well as "NaN" result for some arithmetic operations. + *
+ * In case of JSON, such values can only be handled with non-standard + * processing: for some other formats they can be passed normally. + *
+ * NOTE: this marker is NOT set in case of value overflow/underflow for + * {@code double} or {@code float} values. + * + * @since 2.17 + */ + protected boolean _numberIsNaN; + // And then other information about value itself /** @@ -571,6 +586,7 @@ protected final JsonToken resetInt(boolean negative, int intLen) // May throw StreamConstraintsException: _streamReadConstraints.validateIntegerLength(intLen); _numberNegative = negative; + _numberIsNaN = false; _intLength = intLen; _fractLength = 0; _expLength = 0; @@ -584,6 +600,7 @@ protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, // May throw StreamConstraintsException: _streamReadConstraints.validateFPLength(intLen + fractLen + expLen); _numberNegative = negative; + _numberIsNaN = false; _intLength = intLen; _fractLength = fractLen; _expLength = expLen; @@ -597,17 +614,15 @@ protected final JsonToken resetAsNaN(String valueStr, double value) _textBuffer.resetWithString(valueStr); _numberDouble = value; _numTypesValid = NR_DOUBLE; + _numberIsNaN = true; return JsonToken.VALUE_NUMBER_FLOAT; } @Override public boolean isNaN() throws IOException { - if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { - if ((_numTypesValid & NR_DOUBLE) != 0) { - return !Double.isFinite(_getNumberDouble()); - } - } - return false; + // 01-Dec-2023, tatu: [core#1137] Only return explicit NaN + return (_currToken == JsonToken.VALUE_NUMBER_FLOAT) + && _numberIsNaN; } /* diff --git a/src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java index 1e176918c9..a28f66a66f 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java @@ -58,21 +58,25 @@ public void testNegativeHexadecimal() throws Exception { } } + /** + * Test for checking that Overflow for Double value does not lead + * to bogus NaN information. + */ public void testLargeDecimal() throws Exception { - final String value = "7976931348623157e309"; + final String biggerThanDouble = "7976931348623157e309"; for (int mode : ALL_MODES) { - try (JsonParser p = createParser(mode, " " + value + " ")) { + try (JsonParser p = createParser(mode, " " + biggerThanDouble + " ")) { assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); - assertEquals(new BigDecimal(value), p.getDecimalValue()); + assertEquals(new BigDecimal(biggerThanDouble), p.getDecimalValue()); assertFalse(p.isNaN()); assertEquals(Double.POSITIVE_INFINITY, p.getValueAsDouble()); - // PJF: we might want to fix the isNaN check to not be affected by us reading the value as a double - assertTrue(p.isNaN()); + // 01-Dec-2023, tatu: [core#1137] NaN only from explicit value + assertFalse(p.isNaN()); } } } - //JSON does not allow numbers to have f or d suffixes + // JSON does not allow numbers to have f or d suffixes public void testFloatMarker() throws Exception { for (int mode : ALL_MODES) { try (JsonParser p = createParser(mode, " -0.123f ")) {