mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Apply scale before precision when coercing floats to decimal
Since precision is always larger than scale, it can actually change rounding behavior. Given a precision of 5 and a scale of 3, when you apply the precision of 5 to `1.25047`, the result is `1.2505`, which when the scale is applied would be `1.251` instead of the expected `1.250`. This issue appears to only occur with floats, as scale doesn't apply to other numeric types, and the bigdecimal constructor actually ignores precision entirely when working with strings. There's no way we could handle this for the "unknown object which responds to `to_d`" case, as we can't assume an interface for applying the scale. Fixes #24235
This commit is contained in:
parent
a12ad8ae54
commit
c7d3bd48df
2 changed files with 17 additions and 2 deletions
|
@ -29,12 +29,12 @@ module ActiveModel
|
|||
end
|
||||
end
|
||||
|
||||
scale ? casted_value.round(scale) : casted_value
|
||||
apply_scale(casted_value)
|
||||
end
|
||||
|
||||
def convert_float_to_big_decimal(value)
|
||||
if precision
|
||||
BigDecimal(value, float_precision)
|
||||
BigDecimal(apply_scale(value), float_precision)
|
||||
else
|
||||
value.to_d
|
||||
end
|
||||
|
@ -47,6 +47,14 @@ module ActiveModel
|
|||
precision.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def apply_scale(value)
|
||||
if scale
|
||||
value.round(scale)
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,6 +52,13 @@ module ActiveModel
|
|||
assert_not type.changed?(5.0, 5.0, '5.0')
|
||||
assert_not type.changed?(-5.0, -5.0, '-5.0')
|
||||
end
|
||||
|
||||
def test_scale_is_applied_before_precision_to_prevent_rounding_errors
|
||||
type = Decimal.new(precision: 5, scale: 3)
|
||||
|
||||
assert_equal BigDecimal("1.250"), type.cast(1.250473853637869)
|
||||
assert_equal BigDecimal("1.250"), type.cast("1.250473853637869")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue