Class: Horologium::Numeric::Exact

Inherits:
Object
  • Object
show all
Defined in:
lib/horologium/numeric/exact.rb,
sig/horologium/numeric/exact.rbs

Overview

A number stored as an exact Rational, with no rounding. Where TwoPartFloat trades a little accuracy for the speed of Float arithmetic, Exact keeps the value exactly, as a ratio of two integers.

The value is frozen on creation.

Examples:

An exact third, which no Float can hold

Horologium::Numeric::Exact.new(Rational(1, 3)) ==
  Horologium::Numeric::Exact.new(Rational(1, 3))
# => true

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Exact

Returns a new instance of Exact.

Parameters:

  • value (Integer, Float, Rational)

    the number to store, converted to an exact Rational with to_r. A Float is stored as its exact binary value, which is not always the decimal you typed, so pass a Rational when you mean an exact decimal. Exact records the value it is given faithfully; it does not recover precision already lost before construction.



26
27
28
29
# File 'lib/horologium/numeric/exact.rb', line 26

def initialize(value)
  @value = value.to_r
  freeze
end

Instance Attribute Details

#valueRational (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Protected so == and eql? can read another Exact's Rational.

Returns:

  • (Rational)


94
95
96
# File 'lib/horologium/numeric/exact.rb', line 94

def value
  @value
end

Instance Method Details

#*(scalar) ⇒ Horologium::Numeric::Exact

Parameters:

  • scalar (Integer, Float, Rational)

Returns:

Raises:

  • (ArgumentError)

    when given anything but a plain number



46
47
48
# File 'lib/horologium/numeric/exact.rb', line 46

def *(scalar) # rubocop:disable Naming/BinaryOperatorParameterName
  self.class.new(value * scalar_rational(scalar))
end

#+(other) ⇒ Horologium::Numeric::Exact



33
34
35
# File 'lib/horologium/numeric/exact.rb', line 33

def +(other)
  self.class.new(value + other.value)
end

#-(other) ⇒ Horologium::Numeric::Exact



39
40
41
# File 'lib/horologium/numeric/exact.rb', line 39

def -(other)
  self.class.new(value - other.value)
end

#/(scalar) ⇒ Horologium::Numeric::Exact

Parameters:

  • scalar (Integer, Float, Rational)

Returns:

Raises:

  • (ArgumentError)

    when given anything but a plain number



53
54
55
# File 'lib/horologium/numeric/exact.rb', line 53

def /(scalar) # rubocop:disable Naming/BinaryOperatorParameterName
  self.class.new(value / scalar_rational(scalar))
end

#==(other) ⇒ Boolean

Parameters:

  • other (Object)

Returns:

  • (Boolean)


59
60
61
# File 'lib/horologium/numeric/exact.rb', line 59

def ==(other)
  other.is_a?(self.class) && value == other.value
end

#eql?(other) ⇒ Boolean

Parameters:

  • other (Object)

Returns:

  • (Boolean)


65
66
67
# File 'lib/horologium/numeric/exact.rb', line 65

def eql?(other)
  other.is_a?(self.class) && value.eql?(other.value)
end

#hashInteger

Returns:

  • (Integer)


70
71
72
# File 'lib/horologium/numeric/exact.rb', line 70

def hash
  value.hash
end

#scalar_rational(scalar) ⇒ Rational

An exact value is not a scalar, and passing one where a number belongs is a mistake, so it is refused.

Parameters:

  • scalar (Integer, Float, Rational)

Returns:

  • (Rational)

Raises:

  • (ArgumentError)

    when it is not a plain number



104
105
106
107
108
109
110
111
112
113
# File 'lib/horologium/numeric/exact.rb', line 104

def scalar_rational(scalar)
  case scalar
  when Integer, Float, Rational
    scalar.to_r
  else
    raise ArgumentError,
      "an Exact multiplies and divides by a plain number, " \
      "got a #{scalar.class}"
  end
end

#to_fFloat

The value as a single Float. A Float cannot hold what a Rational holds, so the extra precision is dropped here. Do it at the end, once the arithmetic is done.

Returns:

  • (Float)


84
85
86
# File 'lib/horologium/numeric/exact.rb', line 84

def to_f
  value.to_f
end

#to_rRational

Returns:

  • (Rational)


75
76
77
# File 'lib/horologium/numeric/exact.rb', line 75

def to_r
  value
end