def proportion_exceeding(numbers, threshold)
large_numbers = numbers.find_all {|num| num > threshold}
return large_numbers.size / numbers.size
end
The function is very simple. It takes an array of numbers and a threshold as input, builds a new array by finding all the numbers exceeding the threshold, and then returns the proportion of the large numbers in relation to the original array. But there is a problem:
> proportion_exceeding([1,2,3,4,5,6], 3.5)
=> 0
The function returns 0. What goes wrong is that the division symbol / is being applied to two integers, tries to return the nearest integer to the solution, and so returns 0 for anything less than 1.
> 12/3
=> 4
> 5/3
=> 1
> 1/3
=> 0
Numeric Classes in Ruby
To understand what is going on, we should first remind ourselves of the numeric classes available in Ruby. Numbers come in various forms. We can ask Ruby to tell us the classes by using the class method on different numbers:
> 5.class
Fixnum
> 2.0.class
Float
> (2 ** 1000).class
Bignum
These are the basic three. The Fixnum is your regular integer class, holding numbers up to your machine's word size. Float is similarly your a floating-point class. You can find out the range for the latter using Float::MAX and Float::MIN
> Float::MAX
=> 1.79769313486232e+308
> Float::MIN
=> 2.2250738585072e-308
Finally, the Bignum is used to hold extremely large integers: try printing 2**n for varying sizes of n to see how large a number Ruby can handle!
Division of Numbers
The problem Ruby has with basic arithmetic is that it can be asked to add or subtract or multiply or divide any combination of these numbers:
> 2.class
=> Fixnum
> 3.5.class
=> Float
> 2 + 3.5
=> 5.5
> (2 + 3.5).class
=> Float
When given numbers from two different numbers to add, subtract or multiply together, Ruby will use type conversion to turn all the numbers into their most general form. In the example above, 2 can be represented either as a Fixnum or a Float, so it is turned into a Float to suit the second number. The problem comes with division. Alone of the basic arithmetic operators, division can yield a result of a different type to its arguments: 1 divided by 2 is a half, which is not a Fixnum.
Solution
My solution is to get into the habit of not using '/'. In virtually all cases I prefer an accurate result rather than an integer. Instead, I use the method 'quo', for quotient.
> 1.quo(2)
=> (1/2)
> (1.quo(2)).class
=> Rational
> 1.quo(2) * 2
=> (1/1)
> 1/2 * 2
=> 0
> 2.5.quo(7)
=> 0.357142857142857
If you look at the second call above, you will see there is yet another class of number, called Rational. The rationals are those numbers which can be represented as exact fractions, such as 1/2 or 3/7. The 'quo' method is quite smart. Where possible, it will create a Rational class, which is exact. Where not possible, it will return a Float, as in the last line above. (Apart from 'quo', the other method to remember is 'fdiv' which always returns a Float.)
The revised function looks like:
def proportion_exceeding(numbers, threshold)
large_numbers = numbers.find_all {|num| num > threshold}
return large_numbers.size.quo(numbers.size)
end
> proportion_exceeding([1,2,3,4,5,6], 3.5)
=> (1/2)
> proportion_exceeding([1,2,3,4,5,6], 4.5)
=> (1/3)











