diff options
Diffstat (limited to 'activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb')
-rw-r--r-- | activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb | 62 |
1 files changed, 62 insertions, 0 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 new file mode 100644 index 0000000000..273667fdae --- /dev/null +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -0,0 +1,62 @@ +module ActiveSupport + module NumberHelper + class NumberToRoundedConverter < NumberConverter # :nodoc: + self.namespace = :precision + self.validate_float = true + + def convert + @number = Float(number) + + precision = options.delete :precision + significant = options.delete :significant + + 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 = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros + end + + delimited_number = NumberToDelimitedConverter.convert("%01.#{precision}f" % rounded_number, options) + format_number(delimited_number) + end + + private + + def digits_and_rounded_number(precision) + if number.zero? + [1, 0] + else + digits = digit_count(number) + multiplier = 10 ** (digits - precision) + rounded_number = calculate_rounded_number(multiplier) + digits = digit_count(rounded_number) # After rounding, the number of digits may have changed + [digits, rounded_number] + end + end + + def calculate_rounded_number(multiplier) + (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier + end + + def digit_count(number) + (Math.log10(number.abs) + 1).floor + end + + def strip_insignificant_zeros + options[:strip_insignificant_zeros] + end + + def format_number(number) + if strip_insignificant_zeros + escaped_separator = Regexp.escape(options[:separator]) + number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + else + number + end + end + end + end +end |