Pushing rational numbers
Your usage of Rational is wrong. Should be
mine << Rational(22, 7)
return fraction when dividing with Ruby
Here's a hint:
(8**-2).class #=> Rational
So this already exists in Ruby via the Rational
class, which you can call as a method to create rational numbers.
Rational(5, 3) #=> (5/3)
Rational('5/3') #=> (5/3)
Rational('0.3') #=> (3/10)
Check out the documentation here
Why am I getting rounding errors when using Ruby Rational?
It's not clear to me whether in this expression: current_freq * 2 ** Rational('1/12')
that Ruby is keeping the entire calculation in the Rational realm. Within Ruby, you get:
2.0.0p195 :001 > current_freq = 440
=> 440
2.0.0p195 :002 > current_freq * 2 ** Rational('1/12')
=> 466.1637615180899
The calculation produces a float, not a Rational. If we kept it Rational, it would look like:
2.0.0p195 :005 > Rational( current_freq * 2 ** Rational('1/12'))
=> (4100419809895505/8796093022208)
Even if you do this:
2.0.0p195 :010 > Rational(2) ** Rational(1,12)
=> 1.0594630943592953
Ruby goes from Rational to float. The Ruby doc on Rational doesn't describe this clearly, but the examples given show this when taking a rational to a fractional exponent that's not an integer. This makes sense, since when you take a rational number to a rational (fractional, non-integer) exponent, chances are you're going to get an irrational number. 2**(1/12)
is one of those cases.
So to keep accuracy, you'd need to keep everything in the Rational realm throughout which isn't really possible once you hit an irrational number. You might, as Scott Hunter suggests, be able to narrow the field with some custom functions to control the inaccuracy. It's unclear whether that would be worth the effort in this situation.
Matrix multiplication results in fraction
Your problem has been identified, but your real question has not been answered, namely, when multiplying matrix objects, when is an element of the product an integer, a rational number or a float?
If a
and b
are matrix objects, each element of a*b
will be:
- an integer if all elements of
a
andb
used in its calculation are integers; - a rational number if, among the elements of
a
andb
used in its calculation there is at least one rational number and the remainder are integers or rationals; and - a float if at least one of the elements of
a
andb
used in its calculation is a float.
I will give a few examples, but first let's consider how Ruby expresses rational numbers. A rational number is a number that can be expressed as the ratio of two integers. For example, 1.5
is a rational number since it can be expressed 3/2
. We can't write it that way in Ruby, however, since 3/2
will be replaced by 1
, the result of integer division. Instead, we create an instance of the class Rational:
r = Rational(3,2)
#=> (3/2)
and use that in the calculations. (Note the parentheses in the return value.) We can extract its numerator and denominator, or convert it to an integer (rounding down or up) or a float:
r.numerator
#=> 3
r.denominator
#=> 2
r.to_i
#=> 1
r.ceil
#=> 2
r.to_f
#=> 1.5
Now let's look at some examples.
require 'matrix'
Matrix[[Rational(-1,2)]] * Matrix[[Rational(1,2)]]
#=> Matrix[[(-1/4)]]
Matrix[[-1]] * Matrix[[Rational(1,2)]]
# => Matrix[[(-1/2)]]
Matrix[[-0.5]] * Matrix[[Rational(1,2)]]
#=> Matrix[[-0.25]]
Matrix[[Rational(-1,2), Rational(1,2)]].transpose * Matrix[[Rational(1,2), 0.5]]
#=> Matrix[[(-1/4), -0.25], [(1/4), 0.25]]
Now let's consider the inverse of a matrix:
Matrix[[Rational(-1,2), 1],[2, Rational(2,3)]].inverse
#=> Matrix[[(-2/7), (3/7)], [(6/7), (3/14)]]
Matrix[[Rational(-1,2), 1.0],[2, Rational(2,3)]].inverse
#=> Matrix[[-0.2857142857142857, 0.4285714285714286],
# [0.8571428571428571, 0.21428571428571427]]
In calculating the inverse of a matrix with n
rows and columns there are n
"pivoting" steps. If, as in my latter example, the matrix contains a mix of integers, rationals and floats, when each pivot operation is performed:
- each integer will be converted to a float if at least one float is used in its calculation, else it will be converted to a rational if at least one rational is used in its calculation; else it will remain an integer;
- each rational will be converted to a float if there is at least one float used in its calculation, else it will remain a rational; and
- each float will remain a float.
As "once a float always a float", it won't be long before all elements of the computed matrix are floats. (I believe it can be proven that the inverse will contain all floats if the original matrix contains at least one float.)
Why not use an extended rational instead of floats/doubles?
There are three main reasons "why not". If none of them apply, a rational number system may be useful for your application:
- Compactness. It only takes 8 bytes to store a double, 4 bytes for a float. The is not important if you are dealing with one or two of them. It does matter for matrices with thousands of rows and columns.
- Performance. As Pascal Cuoq pointed out in a comment on the question, many important floating point operations are very fast on modern computers.
- Need to approximate irrational numbers such as
pi
and the values of functions such as square root and the trig functions. If you need to deal with rounding anyway, general rational numbers lose a lot of their attraction. Floating point numbers are, of course, rational, but with carefully designed rounding implemented in fast hardware.
If you think your Rational class has usable compactness and performance for scientific and engineering work, I think you should use it to run the LINPACK benchmarks, especially LINPACK 1000, and post your results for comparison to results for similar systems using floating point arithmetic. LINPACK 1000 times solving a system of 1000 simultaneous linear equations
What does the quo method do?
quo
is a method defined on the Numeric
class (and redefined in the Float
class), which calculates the quotient of the receiver with the given argument. In other words, x.quo(y)
is roughly equivalent to x / y
, but more precise.
The difference here comes in when x
and y
are Fixnum
s (ie. an integer value):
> (1 / 2)
=> 0
> (1 / 2).class
=> Fixnum
> 1.quo(2)
=> (1/2)
> 1.quo(2).class
=> Rational
> 1.quo(2.5)
=> 0.4
> 1.quo(2.5).class
=> Float
Basically, quo
ensures that the result of the division is expressed accurately by returing a Rational
or Float
, depending on the receiver and argument.
In Javascript, there isn't a distinction between different types of numbers, and division returns a floating point number already if necessary, so the first method can be expressed as:
obs * Math.pow(1 - 1 / (9 * obs) - z_value / (3 * Math.sqrt(obs)), 3)
Related Topics
Can't Dup Nilclass on Association Methods
How to Serialize a Ruby Digest::Sha1 Instance Object
How to Count Duplicates Hash Itens in Ruby 1.8.5 ( Sketchup Ruby API )
Ruby on Rails: Create Confirmation View Before Creating the Object
Split Float into Integer and Decimals in Ruby
Dangerousattributeerror in Omniauth Railscast Tutorial: Create Is Defined by Activerecord
Recursive Rails Nested Resources
How to Refer to a Submodule's "Full Path" in Ruby
How to Create a "Clone"-Able Enumerator for External Iteration
Can't Find a Route with an Underscore or Doesn't Treat It Properly
How to Read a Clients Windows Login Name Using Ruby on Rails
Ruby on Rails - £ Sign Troubles
How to Iterate Through This JSON Document Using Ruby
How to Add Iedriverserver to Path
Return True Only If All Values Evaluate to True in Ruby