diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b679d644720d12d4152f4d6e9d35e946ed53c679..252532c7682903c50cac0fdc8d142de6891bb222 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Fix Type casting to Decimal from Float with large precision. + + *Tomohiro Hashidate* + * No verbose backtrace by db:drop when database does not exist. Fixes #16295. diff --git a/activerecord/lib/active_record/type/decimal.rb b/activerecord/lib/active_record/type/decimal.rb index ba5d24472961a2240ccc82b1141f5c54a5dd73a7..d10778eeb60148b98b28fdfc8c6e1636c0c76246 100644 --- a/activerecord/lib/active_record/type/decimal.rb +++ b/activerecord/lib/active_record/type/decimal.rb @@ -14,12 +14,25 @@ def type_cast_for_schema(value) private def cast_value(value) - if value.is_a?(::Numeric) || value.is_a?(::String) + case value + when ::Float + BigDecimal(value, float_precision) + when ::Numeric, ::String BigDecimal(value, precision.to_i) - elsif value.respond_to?(:to_d) - value.to_d else - cast_value(value.to_s) + if value.respond_to?(:to_d) + value.to_d + else + cast_value(value.to_s) + end + end + end + + def float_precision + if precision.to_i > ::Float::DIG + 1 + ::Float::DIG + 1 + else + precision.to_i end end end diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb index 951cd879dd6266873a196a06f09aa95a1e5045fc..da30de373e7fbb1ffeb8da99db0c7bdb47540809 100644 --- a/activerecord/test/cases/type/decimal_test.rb +++ b/activerecord/test/cases/type/decimal_test.rb @@ -10,6 +10,11 @@ def test_type_cast_decimal assert_equal BigDecimal.new("1"), type.type_cast_from_user(:"1") end + def test_type_cast_decimal_from_float_with_large_precision + type = Decimal.new(precision: ::Float::DIG + 2) + assert_equal BigDecimal.new("123.0"), type.type_cast_from_user(123.0) + end + def test_type_cast_decimal_from_rational_with_precision type = Decimal.new(precision: 2) assert_equal BigDecimal("0.33"), type.type_cast_from_user(Rational(1, 3))