Class: Numerals::Rounding

Inherits:
FormattingAspect show all
Includes:
ModalSupport::StateEquivalent
Defined in:
lib/numerals/rounding.rb

Overview

Rounding of Numerals

Constant Summary

DEFAULTS =
{
  mode: :half_even,
  precision: :short,
  places: nil,
  base: 10
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from FormattingAspect

#[], [], aspect, #set, set

Constructor Details

#initialize(*args) ⇒ Rounding

Rounding defines a rounding mode and a precision, and is used to establish the desired accuracy of a Numeral result.

Rounding also defines the base of the numerals to be rounded, which is 10 by default.

The rounding mode is the rule used to limit the precision of a numeral; the rounding modes available are those of Flt::Num, namely:

  • :half_even

  • :half_up

  • :half_down

  • :ceiling

  • :floor

  • :up

  • :down

  • :up05

Regarding the rounding precision there are two types of Roundings:

  • Fixed (limited) precision: the precision of the rounded result is either defined as relative (number of significant digits defined by the precision property) or absolute (number of fractional places –decimals for base 10– defined by the places property)

  • Free (unlimited) precision, which preserves the value of the input numeral. As much precision as needed is used to keep unambiguously the original value. When applied to exact input, this kind of rounding doesn't perform any rounding. For approximate input there are two variants:

    • Preserving the original value precision, which produces and approximate output. (All original digits are preserved; full precision mode). This is the default free precision mode, established by using the :free symbol for the precision (or its synonym :preserve).

    • Simplifiying or reducing the result to produce an exact output without unneeded digits to restore the original value within its original precision (e.g. traling zeros are not keep). This case can be defined with the :short symbol for the precision (or its synonum :simplify).



47
48
49
50
51
52
# File 'lib/numerals/rounding.rb', line 47

def initialize(*args)
  DEFAULTS.each do |param, value|
    instance_variable_set "@#{param}", value
  end
  set! *args
end

Instance Attribute Details

#baseObject

Returns the value of attribute base



61
62
63
# File 'lib/numerals/rounding.rb', line 61

def base
  @base
end

#modeObject

Returns the value of attribute mode



61
62
63
# File 'lib/numerals/rounding.rb', line 61

def mode
  @mode
end

Instance Method Details

#absolute?Boolean

Returns true if the Rounding is of fixed precision defined as a number of fractional places, i.e. independently of the number to be rounded's magnitude.

Returns:

  • (Boolean)


126
127
128
# File 'lib/numerals/rounding.rb', line 126

def absolute?
  @precision.nil? # fixed? && @precision # !@places.nil?
end

#fixed?Boolean

Returns true if the Rounding is of fixed (limited) precision.

Returns:

  • (Boolean)


119
120
121
# File 'lib/numerals/rounding.rb', line 119

def fixed? # limited? approximate? rounding? fixed?
  !free?
end

#free?Boolean

Returns true if the Rounding is of free (unlimited) precision, which can be either :free (preserving) or :short (simplifying) regarding approximate input.

Returns:

  • (Boolean)


114
115
116
# File 'lib/numerals/rounding.rb', line 114

def free? # unlimited? exact? all? nonrounding? free?
  [:free, :short].include?(@precision)
end

#full?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/numerals/rounding.rb', line 157

def full?
  preserving?
end

#inspectObject



107
108
109
# File 'lib/numerals/rounding.rb', line 107

def inspect
  to_s
end

#parametersObject



91
92
93
94
95
96
97
# File 'lib/numerals/rounding.rb', line 91

def parameters
  if @precision
    { mode: @mode, precision: @precision, base: @base }
  else
    { mode: @mode, places: @places, base: @base }
  end
end

#places(value = nil, options = {}) ⇒ Object

Number of fractional places for a given numerical/numeral value If no value is passed, the :places property is returned.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/numerals/rounding.rb', line 183

def places(value = nil, options = {})
  if value.nil?
    @places
  elsif is_exact?(value, options)
    @places || 0
  elsif free?
    num_digits(value, options) - num_integral_digits(value)
  else # fixed?
    if absolute?
      @places
    else # relative?
      @precision - num_integral_digits(value)
    end
  end
end

#places=(v) ⇒ Object



86
87
88
89
# File 'lib/numerals/rounding.rb', line 86

def places=(v)
  @places = v
  @precision = nil if @places
end

#precision(value = nil, options = {}) ⇒ Object

Number of significant digits for a given numerical/numeral value. If no value is passed, the :precision property is returned.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/numerals/rounding.rb', line 163

def precision(value = nil, options = {})
  if value.nil?
    @precision
  elsif free?
    if is_exact?(value, options)
      0
    else
      num_digits(value, options)
    end
  else # fixed?
    if absolute?
      @places + num_integral_digits(value)
    else # relative?
      @precision
    end
  end
end

#precision=(v) ⇒ Object



80
81
82
83
84
# File 'lib/numerals/rounding.rb', line 80

def precision=(v)
  @precision = v
  @precision = :simplify if v == 0
  @places = nil if @precision
end

#preserving?Boolean

Returns true if the Rounding is of free precision and the behaviour for approximate numbers is to keep its original precision (so it may include trailing zeros) and the result of rounding will be an approximate numeral.

Returns:

  • (Boolean)


153
154
155
# File 'lib/numerals/rounding.rb', line 153

def preserving?
  @precision == :free
end

#relative?Boolean

Returns true if the Rounding is of fixed precision defined as a number of significant digits (precision attribute), i.e. in relation to the number to be rounded's magnitude.

Returns:

  • (Boolean)


133
134
135
# File 'lib/numerals/rounding.rb', line 133

def relative?
  fixed? && !absolute?
end

#round(numeral, options = {}) ⇒ Object

Round a numeral. If the numeral has been truncated the :round_up option must be used to pass the information about the discarded digits:

  • nil if all discarded digits where 0 (the truncated value is exact)

  • :lo if there where non-zero discarded digits, but the first discarded digit is below half the base.

  • :tie if the first discarded was half the base and there where no more nonzero digits, i.e. the original value was a 'tie', exactly halfway between the truncated value and the next value with the same number of digits.

  • :hi if the original value was above the tie value.



209
210
211
212
213
214
215
216
217
# File 'lib/numerals/rounding.rb', line 209

def round(numeral, options={})
  round_up = options[:round_up]
  numeral, round_up = truncate(numeral, round_up)
  if numeral.exact?
    numeral
  else
    adjust(numeral, round_up)
  end
end

#short?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/numerals/rounding.rb', line 145

def short?
  simplifying?
end

#simplifying?Boolean

Returns true if the Rounding is of free precision and the behaviour for approximate numbers is producing a simplified (short) result with only the needed digits to restore the original value within its precision.

Returns:

  • (Boolean)


141
142
143
# File 'lib/numerals/rounding.rb', line 141

def simplifying?
  @precision == :short
end

#to_sObject



99
100
101
102
103
104
105
# File 'lib/numerals/rounding.rb', line 99

def to_s
  params = parameters
  DEFAULTS.each do |param, default|
    params.delete param if params[param] == default
  end
  "Rounding[#{params.inspect.unwrap('{}')}]"
end