mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix AS::NumberHelper results with large precisions
before: ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50 => "3.14158999999999988261834005243144929409027099609375" after: ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50 => "3.14159000000000000000000000000000000000000000000000"
This commit is contained in:
parent
c437a98aca
commit
9e997e9039
3 changed files with 62 additions and 7 deletions
|
@ -1,3 +1,14 @@
|
|||
* Fixed Float related error in NumberHelper with large precisions.
|
||||
|
||||
before:
|
||||
ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50
|
||||
#=> "3.14158999999999988261834005243144929409027099609375"
|
||||
after:
|
||||
ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50
|
||||
#=> "3.14159000000000000000000000000000000000000000000000"
|
||||
|
||||
*Kenta Murata*, *Akira Matsuda*
|
||||
|
||||
* Default the new `I18n.enforce_available_locales` config to `true`, meaning
|
||||
`I18n` will make sure that all locales passed to it must be declared in the
|
||||
`available_locales` list.
|
||||
|
|
|
@ -5,28 +5,50 @@ module ActiveSupport
|
|||
self.validate_float = true
|
||||
|
||||
def convert
|
||||
@number = Float(number)
|
||||
|
||||
precision = options.delete :precision
|
||||
significant = options.delete :significant
|
||||
|
||||
case number
|
||||
when Float, String
|
||||
@number = BigDecimal(number.to_s)
|
||||
when Rational
|
||||
if significant
|
||||
@number = BigDecimal(number, digit_count(number.to_i) + precision)
|
||||
else
|
||||
@number = BigDecimal(number, precision)
|
||||
end
|
||||
else
|
||||
@number = number.to_d
|
||||
end
|
||||
|
||||
if significant && precision > 0
|
||||
digits, rounded_number = digits_and_rounded_number(precision)
|
||||
precision -= digits
|
||||
precision = 0 if precision < 0 # don't let it be negative
|
||||
else
|
||||
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
|
||||
rounded_number = number.round(precision)
|
||||
rounded_number = rounded_number.to_i if precision == 0
|
||||
rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
|
||||
end
|
||||
|
||||
delimited_number = NumberToDelimitedConverter.convert("%01.#{precision}f" % rounded_number, options)
|
||||
formatted_string =
|
||||
case rounded_number
|
||||
when BigDecimal
|
||||
s = rounded_number.to_s('F') + '0'*precision
|
||||
a, b = s.split('.', 2)
|
||||
a + '.' + b[0, precision]
|
||||
else
|
||||
"%01.#{precision}f" % rounded_number
|
||||
end
|
||||
|
||||
delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
|
||||
format_number(delimited_number)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def digits_and_rounded_number(precision)
|
||||
if number.zero?
|
||||
if zero?
|
||||
[1, 0]
|
||||
else
|
||||
digits = digit_count(number)
|
||||
|
@ -38,11 +60,11 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
def calculate_rounded_number(multiplier)
|
||||
(BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
|
||||
(number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier
|
||||
end
|
||||
|
||||
def digit_count(number)
|
||||
(Math.log10(number.abs) + 1).floor
|
||||
(Math.log10(absolute_number(number)) + 1).floor
|
||||
end
|
||||
|
||||
def strip_insignificant_zeros
|
||||
|
@ -57,6 +79,14 @@ module ActiveSupport
|
|||
number
|
||||
end
|
||||
end
|
||||
|
||||
def absolute_number(number)
|
||||
number.respond_to?(:abs) ? number.abs : number.to_d.abs
|
||||
end
|
||||
|
||||
def zero?
|
||||
number.respond_to?(:zero?) ? number.zero? : number.to_d.zero?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -123,6 +123,12 @@ module ActiveSupport
|
|||
assert_equal("10.00", number_helper.number_to_rounded(9.995, :precision => 2))
|
||||
assert_equal("11.00", number_helper.number_to_rounded(10.995, :precision => 2))
|
||||
assert_equal("0.00", number_helper.number_to_rounded(-0.001, :precision => 2))
|
||||
|
||||
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(111.2346, :precision => 20))
|
||||
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(Rational(1112346, 10000), :precision => 20))
|
||||
assert_equal("111.23460000000000000000", number_helper.number_to_rounded('111.2346', :precision => 20))
|
||||
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(BigDecimal(111.2346, Float::DIG), :precision => 20))
|
||||
assert_equal("111.2346" + "0"*96, number_helper.number_to_rounded('111.2346', :precision => 100))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -155,6 +161,14 @@ module ActiveSupport
|
|||
assert_equal "10.0", number_helper.number_to_rounded(9.995, :precision => 3, :significant => true)
|
||||
assert_equal "9.99", number_helper.number_to_rounded(9.994, :precision => 3, :significant => true)
|
||||
assert_equal "11.0", number_helper.number_to_rounded(10.995, :precision => 3, :significant => true)
|
||||
|
||||
assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775, :precision => 20, :significant => true )
|
||||
assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775.0, :precision => 20, :significant => true )
|
||||
assert_equal "9775.0000000000000000", number_helper.number_to_rounded(Rational(9775, 1), :precision => 20, :significant => true )
|
||||
assert_equal "97.750000000000000000", number_helper.number_to_rounded(Rational(9775, 100), :precision => 20, :significant => true )
|
||||
assert_equal "9775.0000000000000000", number_helper.number_to_rounded(BigDecimal(9775), :precision => 20, :significant => true )
|
||||
assert_equal "9775.0000000000000000", number_helper.number_to_rounded("9775", :precision => 20, :significant => true )
|
||||
assert_equal "9775." + "0"*96, number_helper.number_to_rounded("9775", :precision => 100, :significant => true )
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue