lib/numerals/conversions.rb in numerals-0.2.1 vs lib/numerals/conversions.rb in numerals-0.3.0

- old
+ new

@@ -75,18 +75,110 @@ # # The number is treated as an exact value, and converted according to # Rounding. (in this case the :free and :short precision roundings are # equivalent) # + # Summary + # + # In result there are 5 basically diferent conversion modes. + # Three of them apply only to approximate values, so they are + # not available for all input types: + # + # * 'Short' mode, which produces an exact Numeral. Used when + # input is not exact and rounding precision is :short. + # * 'Free' mode, which produces an approximate Numeral. Used + # when input is not exact and rounding precision is :short. + # * 'Fixed' mode, which produces an approximate Numeral. Used when + # input isnot exact and rounding precision is limited. + # + # The other two modes are applied to exact input, so they're + # available for all input types (since all can be taken as exact with + # the :exact option): + # + # * 'All' mode, which produces an exact Numeral. Used when + # input is exact and rounding precision is :free (or :short). + # * 'Rounded' mode, which produces an approximate Numeral. Used + # when input is exact and rounding precision is limited. + # def write(number, options = {}) output_rounding = Rounding[options[:rounding] || Rounding[]] conversion = self[number.class, options[:type_options]] exact_input = conversion.exact?(number, options) conversion.write(number, exact_input, output_rounding) end def exact?(number, options = {}) self[number.class, options[:type_options]].exact?(number, options) + end + + # Convert an number to a different numeric type. + # Conversion is done by first converting the number to a Numeral, + # then converting the Numeral to de destination type. + # + # Options: + # + # * :exact_input Consider the number an exact quantity. + # Otherwise, for approximate types, insignificant digits + # will not be converted. + # * :rounding Rounding to be applied during the conversion. + # * :type or :context can be used to define the destination + # type. + # * :output_mode can have the values :free, :short or :fixed + # and is used to define how the result is generated. + # + def convert(number, options = {}) + if options[:exact] + options = options.merge(exact_input: true, ouput_mode: :free) + end + + exact_input = options[:exact_input] || false + rounding = Rounding[options[:rounding] || Rounding[]] + output_mode = options[:output_mode] || :free # :short :free :fixed + type_options = options[:type_options] + selector = options[:context] || options[:type] + output_conversions = self[selector, type_options] + if output_conversions && output_conversions.respond_to?(:context) + output_base = output_conversions.context.radix + if rounding.base != output_base && rounding.free? + rounding = rounding[base: output_base] + end + end + + if number.is_a?(Numeral) + numeral = number + else + input_options = { + exact: exact_input, + rounding: rounding, + type_options: type_options + } + numeral = write(number, input_options) + end + + output_options = { + type: options[:type], context: options[:context] + } + case output_mode + when :short + output_options.merge!( + exact: false, + simplify: true + ) + when :free + output_options.merge!( + exact: false, + simplify: false + ) + when :fixed + output_options.merge!( + exact: true, + simplify: false + ) + end + if !output_options[:exact] && numeral.exact? + numeral.approximate! + end + read(numeral, output_options) end private def extract_mode_from_args!(args)