Output formatting differences between jRuby 10.0.0.1 and Ruby 3.4 when converting Float to JSON.
In Ruby 3.4, when converting a Float value to JSON using .to_json, the output notation is more or less preserved. Namely, if your value is in scientific notation, the output will be in scientific notation. If it's decimal, it will remain decimal, though there does appear to be a precision limit where it will truncate additional decimals.
In jRuby 10.0.0.1, Float values above a certain size will always be converted to scientific notation in the output. The precision doesn't appear to be the culprit, it seems to be the number of digits to the LEFT of the decimal that determines this. If there are more than 7, it will convert the notation.
For example (in jRuby 10):
irb(main):001> x = 1234567.1234567893
=> 1234567.1234567894
irb(main):002> x.to_json
=> "1234567.1234567894"
irb(main):003> x = 12345678.12345678912
=> 12345678.12345679
irb(main):004> x.to_json
=> "1.234567812345679E7"
Versus (in Ruby 3.4.1):
irb(main):001> x = 1234567.1234567893
=> 1234567.1234567894
irb(main):002> x.to_json
=> "1234567.1234567894"
irb(main):003> x = 12345678.12345678912
=> 12345678.12345679
irb(main):004> x.to_json
=> "12345678.12345679"
In particular, this causes issues with JSON output containing timestamps in epoch+milliseconds output, such as what you would get from Time.now.to_f.
This could be a behavior difference in JRuby, but without looking into how the json library jsonifies floats, I figured it best to file here.
Likely caused by this line, which uses Java's default formatting options for floats:
https://github.com/ruby/json/blob/c079793b7655b749a4d85f5c8e6bd2649fd31c0c/java/src/json/ext/Generator.java#L408
Naive fix would be to just call RubyFloat.to_s but that will create some transient garbage:
https://github.com/jruby/jruby/blob/166d1426e26329458dde1fa930992f088ee6f69d/core/src/main/java/org/jruby/RubyFloat.java#L270-L302
Better would be to call the equivalent logic and just get a ByteList or even get the float to write itself to the stream with minimal garbage.
So... this is honestly a bit of a non-issue issue. Yes, the behavior differs from MRI Ruby, so it should probably be fixed, but that scientific notation in the output is still a valid number in Javascript (all numbers in JS are double-precision floats), so ultimately gets resolved the same. In my particular case, the only thing that was actually breaking was specs that were looking for a particular raw JSON value.