Numerals
The Numerals module provides formatted input/output for numeric types.
Use
require 'numerals'
include Numerals
The Numeral class is used internally to hold the representation of a numeric quantity as numeral in a positional system. Since repeating figures are supported in Numeral, a Numeral can represent exactly any rational number.
Numerals can be exact or approximate. Exact numerals don't keep trailing zeros: they don't specify a fixed precision. Repeating numerals are always exact.
Approximate numerals may have trailing zeros and have a determinate number of significant digits. Approximate numerals cannot held repeating figures, since they have limited precision.
The Conversions module provides conversions between Numerals and the numeric types Integer, Rational, Float, Flt::Num and BigDecimal.
The Format class holds formatting settings. It can be constructed with this bracket syntax:
format = Format[mode: :general, rounding: [precision: 10]]
Which is a shortcut for:
format = Format[mode: Format::Mode[:general], rounding: Rounding[precision: 10]]
And can also be expressed as:
format = Format[Format::Mode[:general], Rounding[precision: 10]]
puts format.rounding.precision # -> 10
puts format.rounding.mode # -> half_even
New formats can be derived from an existing one by overriding some of its properties using the brackets operator on it:
format2 = format[rounding: :half_down]
puts format2.rounding.precision # -> 10
puts format2.rounding.mode # -> half_down
Output
Let's see how to use a Format to format a number into text form. By default the shortest possible output (that preserves the value) is produced:
puts Format[].write(0.1) # -> 0.1
This is because the default Rounding property of Format is Rounding[:short] (rounding to :short precision), so the above is equivalent to:
puts Format[:short].write(0.1) # -> 0.1
puts Format[rounding: :short].write(0.1) # -> 0.1
To produce a numeric representation that shows explicitly all the precision of the number, the :free rounding precision can be used:
puts Format[:free].write(0.1) # -> 0.10000000000000001
Specific precision can be obtained like this:
puts Format[precision: 6].write(0.1) # -> 0.100000
But this won't show digits that are insignificant (when the input number is regarded as an approximation):
puts Format[precision: 20].write(0.1) # -> 0.10000000000000001
puts Format[precision: 20].write(Rational(1,10)) # -> 0.10000000000000000000
Although a Float is considered an approximation by default (since it cannot represent arbitrary precision exactly), we can reinterpret it as an exact quantity with the :exact_input Format option:
puts Format[:exact_input, precision: 20].write(0.1)
# -> 0.10000000000000000555
puts Format[:exact_input].write(0.1)
# -> 0.1000000000000000055511151231257827021181583404541015625
Rationals are always 'exact' quantities, and they may require infinite digits to be represented exactly in some output bases. This is handled by repeating numerals, which can be represented as text in two modes:
puts Format[].write(Rational(1,3)) # -> 0.333...
puts Format[symbols: [repeat_delimited: true]].write(Rational(1,3))# -> 0.<3>
Input
The same Format class can be used to read formatted text into numeric values with the Format#read() method.
puts Format[].read('1.0', type: Float) # -> 1.0
For Flt types such as Flt::DecNum or Flt::BinNum there are a few options to determine the result, since these types can hold arbitrary precision.
By default, these types are considered 'approximate'. Thus, the result will be a variable-precision result based on the input. The default Format, which has :short (simplifying) precision will produce a simple result with as few significant digits as possible:
puts Format[:short].read('1.000', type: Flt::DecNum) # -> 1
puts Format[:short].read('0.100', type: Flt::BinNum) # -> 0.1
To retain the precision of the input text, the :free precision should be used:
puts Format[:free].read('1.000', type: Flt::DecNum) # -> 1.000
puts Format[:free].read('0.100', type: Flt::BinNum) # -> 0.1
As an alternative, the precision implied by the text input can be ignored and the result adjusted to the precision of the destination context. This is done by regarding the input as 'exact'.
puts Format[:exact_input].read('1.000', type: Flt::DecNum) # -> 1.000000000000000000000000000
puts Format[:exact_input].read('0.100', type: Flt::BinNum) # -> 0.1
Flt::DecNum.context.precision = 8
puts Format[:exact_input].read('1.000', type: Flt::DecNum) # -> 1.0000000
If the input specifies repeating digits, then it is automatically regarded exact and rounded according to the destination context:
puts Format[:exact_input].read('0.333...', type: Flt::DecNum) # -> 0.33333333
Note that the repeating digits have been automatically detected. This happens because the repeating suffix '...' has ben found (it is defined by the Format::Symbols property of Format). An alternative way of specifying repeating digits is by the repeating delimiters specified in Symbols, which are <> by default:
puts Format[:exact_input].read('0.<3>', type: Flt::DecNum)# -> 0.33333333
A Format can also be used to read a formatted number into a Numeral:
puts Format[].read('1.25', type: Numeral)
# -> Numeral[1, 2, 5, :sign=>1, :point=>1, :normalize=>:approximate, :base=>10]
puts Format[].read('1.<3>', type: Numeral)
# -> Numeral[1, 3, :sign=>1, :point=>1, :repeat=>1, :base=>10]
Other examples:
puts Format[:free, base: 2].read('0.1', type: Flt::DecNum)# -> 0.5
Shortcut notation
The <<
and >>
operators can be applied to Format objects
as a shortcut for formatted writing and reading:
fmt = Format[]
puts fmt << 0.1 # -> 0.1
puts fmt << 0.1 << ' ' << 0.2 << ' ' << [places: 3] << 0.3 # -> 0.1 0.2 0.300
puts fmt >> '0.1' >> Rational # -> 1/10
These operators can also be applied to Format, which is equivalent
to apply them to de default Format, Format[]
:
puts Format << [:sci, places: 4] << 0.1 # -> 1.000e-1
puts Format >> '0.1' >> Rational # -> 1/10
Roadmap
Done:
-
Numeral handles (repeating) numerals in any base with bidirectional quotient conversion.
-
Numerical conversions (numbers to/from Numerals)
-
Rounding can be applied to Numerals (with rounding options)
-
Numerals can be written into text form using Formatting options
-
Numerals con be read from text form using Formatting options
-
Handling of 'unsignificant' digits: show them either as special symbol, as zeros or omit them (comfigured in Symbols)
-
Padding aspect of formatting on output
-
Show base indicators on output
Pending:
- HTML & Latex Input/Output