New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BigDecimal/float difference between MRI and JRuby #5904
Comments
I think that this issue should be closed since this is probably implementation dependent, and should not be something that a programmer should rely on. |
Looking into this a little big (apparently for the first time)... I believe this is an artifact of how we compare a BigDecimal with a Float. In JRuby, we promote the Float into a BigDecimal by using the jruby/core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java Lines 495 to 509 in 57f9668
Duplicating this code from Ruby we see the same result:
Leaving the precision off gets us a result that matches CRuby:
As near as I can tell this change came in 73764ea as part of improving the coercion process for Float values. I am not sure where the 15 precision came from, but if we revisit CRuby's equivalent to |
The CRuby I believe this means it will end up using infinite precision based on the normalized Integer form of the Float which would explain the behavioral difference from our 15 precision logic. As of Java 8, the java.math.BigDecimal logic does not convert the incoming double value into a String. Instead it works with the raw bits directly and pulls out each element of the floating-point value to use in constructing the BigDecimal instance. The value is then rounded to the requested precision, which results in the exact BigDecimal So basically, by providing the precision and allowing it to round off, we are choosing one inaccurate representation over another, and end up matching. @ahorek I believe this was your commit. I know it has been a while, but do you remember why you chose to pass in a MathContext with a precision here? The fix for this would be to figure out what precision and rounding behavior matches CRuby's float-to-string-to-integer path. |
actually 73764ea didn't cause the regression.
the idea behind this is that more accurate precision than the maximum float precision doesn't matter for a comparsion with another float. the flaw is we're not comparing the real float representation which is inacurrate.
so the value should be normalized before converting to a bigdecimal. The precision isn't a real problem. also the way how CRuby do it seems to be complex and probably slow. The best way would be to use the fast path which works for most cases and do an additional validation step if there's a match for corner cases like this. btw: I don't see a real use case why should anyone depend on precise comparsions between floats and bigdecimals. |
You are correct. I did not mean to imply that you broke anything. It is largely a bug in us following CRuby logic for the comparison but not for the "GetVpValue" logic that converts float to BigDecimal via a string. I think we will need to diverge on the implementation of RubyBigDecimal.cmp in order to do more accurate comparisons. Truth be told, we could probably drop most of the ported logic from CRuby if we can get the JDK BigDecimal to do the equivalent work. At this point I am not sure what the fix should be, or indeed whether a fix is necessary. These values are logically equal but physically unequal, and every resource I have looked at recommends against comparing floating-point values and BigDecimal values without explicitly opting into the rounding. In this case we are rounding to 15 digits which causes them to appear equal, while 16 would cause them to be unequal:
Even if we were to skip this coercion to BigDecimal at a Ruby level, we would still need to coerce to a JDK BigDecimal in order to do the comparison, at which point we would need to choose some level of rounding. |
Expected Behavior
In MRI the following comparison returns false:
This is probably because there is no exact floating point representation for the number 8.03 (as opposed to e.g. 8.02 and 8.04, for which the similar comparison returns
true
.Actual Behavior
This is not a problem for me, except that it was surprising that a spec passed in JRuby that I expected to fail.
Environment
The text was updated successfully, but these errors were encountered: