Problem
The trigonometry functions in Ruby's Math library take input in radians (2π radians in a circle). Most real-world applications measure angles in degrees (360 degrees in a circle). You want an easy way to do trigonometry with degrees.
Solution
The simplest solution is to define a conversion method in Numeric that will convert a number of degrees into radians.
class Numeric def degrees self * Math::PI / 180 end end
You can then treat any numeric object as a number of degrees and convert it into the corresponding number of radians, by calling its degrees method. Trigonometry on the result will work as you'd expect:
90.degrees # => 1.5707963267949 Math::tan(45.degrees) # => 1.0 Math::cos(90.degrees) # => 6.12303176911189e-17 Math::sin(90.degrees) # => 1.0 Math::sin(89.9.degrees) # => 0.999998476913288 Math::sin(45.degrees) # => 0.707106781186547 Math::cos(45.degrees) # => 0.707106781186548
Discussion
I named the conversion method degrees by analogy to the methods like hours defined by Rails. This makes the code easy to read, but if you look at the actual numbers, it's not obvious why 45.degrees should equal the floating-point number 0.785398163397448.
If this troubles you, you could name the method something like degrees_to_radians. Or you could use Lucas Carlson's units gem, which lets you define customized unit conversions, and tracks which unit is being used for a particular number.
require 'rubygems' require 'units/base' class Numeric remove_method(:degrees) # Remove the implementation given in the Solution add_unit_conversions(:angle => { :radians => 1, :degrees => Math::PI/180 }) add_unit_aliases(:angle => { :degrees => [:degree], :radians => [:radian] }) end 90.degrees # => 90.0 90.degrees.unit # => :degrees 90.degrees.to_radians # => 1.5707963267949 90.degrees.to_radians.unit # => :radians 1.degree.to_radians # => 0.0174532925199433 1.radian.to_degrees # => 57.2957795130823
The units you define with the units gem do nothing but make your code more readable. The trigonometry methods don't understand the units you've defined, so you'll still have to give them numbers in radians.
# Don't do this: Math::sin(90.degrees) # => 0.893996663600558 # Do this: Math::sin(90.degrees.to_radians) # => 1.0
Of course, you could also change the trigonometry methods to be aware of units:
class << Math alias old_sin sin def sin(x) old_sin(x.unit == :degrees ? x.to_radians : x) end end 90.degrees # => 90.0 Math::sin(90.degrees) # => 1.0 Math::sin(Math::PI/2.radians) # => 1.0 Math::sin(Math::PI/2) # => 1.0
That's probably overkill, though.
See Also
Strings
Numbers
Date and Time
Arrays
Hashes
Files and Directories
Code Blocks and Iteration
Objects and Classes8
Modules and Namespaces
Reflection and Metaprogramming
XML and HTML
Graphics and Other File Formats
Databases and Persistence
Internet Services
Web Development Ruby on Rails
Web Services and Distributed Programming
Testing, Debugging, Optimizing, and Documenting
Packaging and Distributing Software
Automating Tasks with Rake
Multitasking and Multithreading
User Interface
Extending Ruby with Other Languages
System Administration