Search code examples
rubypi

How to calculate PI in Ruby?


I have to get the exact amount of decimal digits of pi, so I tried using this method:

require "bigdecimal/math"
include BigMath
input = 9
output = BigMath.PI(input).to_s
puts output

but instead of getting this as a result:

output: 3.141592653 # Which are the first 9 decimal digits of PI

I get this:

output: 0.3141592653589793238462643313830947690870926e1

What am I doing wrong?


Solution

  • Since π is an irrational number, its decimal representation never ends and never settles into a permanently repeating pattern.

    Therefore, algorithms for calculation of π can find only results with specified precision.

    BigMath.PI - is not an exception.

    Parameter prec which we pass as a first argument, means precision (that is the number of accurate digits after the decimal point).

    When we execute BigMath.PI(9), we obtain a BigDecimal instance, which string representation is 0.3141592653589793238462643313830947690870926e1.

    If we look more closely at this string we can see e1 in the end.

    Such notation is common in math and it means:

    E-notation Image

    Hence, in our case

    0.3141592653589793238462643313830947690870926e1
    

    is the same as

    0.3141592653589793238462643313830947690870926 * 10 = 3.141592653589793238462643313830947690870926
    

    Since we passed 9 to BigMath.PI, we have at least 9 accurate digits after the decimal dot

    3.141592653589793238462643313830947690870926
    3.141592653_________________________________
    

    (Actually, when we pass 9 to BigMath.PI, it returns more than 9 accurate digits after the decimal dot, but we should not rely on this fact. You can try to compare if you like).

    And probably the last thing to mention: we can't just convert BigDecimal to Float, if we care about accuracy, because Float in general stores only 15 digits after the decimal dot.

    So, if your goal is to be able to show π with an arbitrary number of digits after the decimal dot, you can use the following method:

    require 'bigdecimal/math'
    
    ##
    # Returns PI as a string with number_of_digits_after_decimal_dot.
    #
    def pi(number_of_digits_after_decimal_dot: 2)
      # Let's assume presicion equals to 2 as an example
      precision = number_of_digits_after_decimal_dot
    
      result = BigMath.PI(precision)
      # => ВigDecimal 0.31415926535897932384671233672993238432e1
    
      result = result.truncate(precision).to_s
      # => String 0.314e1
    
      # Remove '0.'
      result = result[2..-1]
      # => String 3141e1
    
      # Remove 'e1'
      result = result.split('e').first
      # => String 3141
    
      result = result.insert(1, '.')
      # => String 3.141
    
      result
    end
    

    If not, then, please, update your question to make it less ambiguous.