Skip to content

Upstream Bug: JsonNumber.of(double) hardcodes decimalOffset causing toLong to fail #118

@simbo1905

Description

@simbo1905

Summary

The JsonNumber.of(double) factory method has a bug where it hardcodes decimalOffset=0 and exponentOffset=0 when creating the JsonNumberImpl. This causes toLong() to fail for integral doubles like 123.0.

Root Cause

In JsonNumber.java line 117:

static JsonNumber of(double num) {
    if (!Double.isFinite(num)) {
        throw new IllegalArgumentException("Not a valid JSON number");
    }
    var str = Double.toString(num);
    return new JsonNumberImpl(str.toCharArray(), 0, str.length(), 0, 0);
    //                                                           ^^^^^ BUG: hardcoded offsets
}

For Double.toString(123.0) which returns "123.0":

  • decimalOffset should be 3 (position of .) but is passed as 0
  • This causes initNumLong() in JsonNumberImpl to misparse the number

Reproduction

var jn = JsonNumber.of(123.0);
jn.toString();  // "123.0" - OK
jn.toDouble();  // 123.0 - OK
jn.toLong();    // THROWS: "123.0 cannot be represented as a long"
                // EXPECTED: 123L (per API docs, "123.0" should be convertible)

Comparison with Json.parse()

var parsed = Json.parse("123.0");
parsed.toLong();  // 123L - WORKS correctly

The difference is that Json.parse() correctly computes the decimalOffset during parsing.

Suggested Fix

Option 1 - Reuse JsonNumber.of(String) which delegates to parser:

static JsonNumber of(double num) {
    if (!Double.isFinite(num)) {
        throw new IllegalArgumentException("Not a valid JSON number");
    }
    return JsonNumber.of(Double.toString(num));
}

Option 2 - Compute correct offsets:

static JsonNumber of(double num) {
    if (!Double.isFinite(num)) {
        throw new IllegalArgumentException("Not a valid JSON number");
    }
    var str = Double.toString(num);
    var chars = str.toCharArray();
    int dec = str.indexOf('.');
    int exp = str.indexOf('e');
    if (exp == -1) exp = str.indexOf('E');
    return new JsonNumberImpl(chars, 0, chars.length, 
        dec == -1 ? -1 : dec, 
        exp == -1 ? -1 : exp);
}

This is an Upstream Bug

This bug exists in OpenJDK jdk-sandbox json branch at commit 91a479d. The upstream tests in TestJsonNumber.java do not cover this case - they test of(double).toDouble() but never of(double).toLong() for integral doubles.

Affected Versions

  • jdk-sandbox json branch (upstream)
  • java.util.json.Java21 backport 0.1.9 (this repo, synced from upstream)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions