Beginner rubyists are spoiled while they first learn the language when it comes to type conversions. Ruby has many built in methods for any conversions you could think of, and it allows you to forget about what is actually happening under the hood. I found that even with a strong understanding of ruby syntax, when dealing with floating points I had unexpected results. I'm going to go over simple number conversions in ruby, and then go into some detail on what is actually happening.
Ruby offers simple ways to change numbers from the integer class to the floating point class, and back again. Using
to_i, you can change any number into an instance of the integer class. Likewise, you can use
to_f to convert any number into a floating point number or float class. Floats include decimal places, and are considered inexact number in ruby. I talk about inexect numbers later on.
10.0.to_i #10 10.to_f #10.0 Integer(10.0) #10 Float(10) #10.0
Great! You can see that
Float() act the same way as
to_f. Ruby also allows you to change strings to numbers using
to_i. This is particularly useful if you are receiving input from a user or a document. When there are non-number characters involved, ruby strips characters after the number, and just leaves you the number. If the numbers fall in the middle or after other characters, ruby will not recognize those numbers, and returns 0. Additionally, if a number in a string has a decimal, it will need to be converted to a float to keep those digits.
"76 Trombones!".to_i #76 "76 Trombones!".to_f #76.0 "AB72JP".to_i #0 "AB72JP".to_f #0.0 "32 and 48".to_i #32 "32 and 48".to_f #32.0 "4.89".to_i #4 "4.89".to_f #4.89 76.to_s #"76"
Everything seems to behave as expected when converting from strings, and when converting back to a string, we can just use
to_s. Let's look at
to_i again in regards to floats.
4.8.to_i #4 4.99999.to_i #4
For floating points, the
to_i conversion truncates the number, meaning that there is no rounding. Even in the case of 4.99999, it still converts it to 4. What if we want to round to the nearest integer? Ruby has a built in function
round() which allows us to both change floats to integers, and round floats to decimal places.
round() with no argument will round to 0 decimals, which will return an integer type number. Using
round(1) will round to one decimal, and
round(2) will round to two decimals.
4.8.round() #5 4.99999.round(0) #5 4.325.round(2) #4.33 4.99999.round(2) #5.0 4.2.round #4
At this point, I hope you are feeling confident about converting numbers in ruby. Another case of implicit conversions can be in doing math with integers and floats. When you divide two integers using
/, it will be default return a whole number quotient. If you want the remainder, you can use the modulus,
%. If you were hoping for a decimal number, then you will need to have at least one floating point number in the operation.
17/4 #4 17%4 #1 17.0/4 #4.25 17/(4.to_f) #4.25 (17/4).to_f #4.0
Make sure that you convert to float before you divide, otherwise you will end up with the wrong results. I mentioned earlier that float type numbers are inexact. This makes doing more complicated math difficult using floats. Let's look at an example where this is going to effect your code.
b = 1 - 0.9 #0.09999999999999998 a = 0.1 #0.1 a == b #false 0.4 + 0.2 #0.6000000000000001
1 - 0.9 not equal
0.1? This is the inexactness of floats. Becuase of how they are stored, when using them for math, you start to build up errors in the numbers. What is the alternative to using floats? In Ruby, you can use a class called BigDecimal. This is part of Ruby, but you will need to add
require "BigDecimal" to the top of your code.
require "BigDecimal" BigDecimal("76582.124") #0.76582124e5 b = BigDecimal("0.9") #0.9e0 a = BigDecimal("1.0") #0.1e1 c = 0.1 (a - b) == c #true (a - b) #0.1e0 (a - b).to_f #0.1
With BigDecimal, numbers are stored in scientific notation, where the e represents the decimal times 10 to the power after the e. This allows you to work to a precision that isn't available when using floats.
Hopefully this guide helped you understand ruby number more easily. There are many other nuances you will encounter, but this is a good starting point for most projects.