diff options
author | Kenta Murata & Akira Matsuda <mrkn@cookpad.com> | 2013-12-20 15:35:13 +0900 |
---|---|---|
committer | Akira Matsuda <ronnie@dio.jp> | 2013-12-20 16:26:35 +0900 |
commit | 9e997e9039435617b6a844158f5437e97f6bc107 (patch) | |
tree | 2d1e8853e3ac9b5e45fc5e06ad3ce667e6675401 /activesupport/lib/active_support/number_helper | |
parent | c437a98acaf759d53a85151947709f5efe499a27 (diff) | |
download | rails-9e997e9039435617b6a844158f5437e97f6bc107.tar.gz rails-9e997e9039435617b6a844158f5437e97f6bc107.tar.bz2 rails-9e997e9039435617b6a844158f5437e97f6bc107.zip |
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"
Diffstat (limited to 'activesupport/lib/active_support/number_helper')
-rw-r--r-- | activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb | 44 |
1 files changed, 37 insertions, 7 deletions
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index 273667fdae..c42354fc83 100644 --- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -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 |