Search code examples
schemeocamldivide-by-zeroeulers-number

Calculating Euler's constant with OCaml


I started learning OCaml today. I already knew Scheme so I thought it would be good to try to translate some example Scheme programs to ML. I have some code below for calculating Euler's number which works, but it does not work in OCaml. I get this error: Exception: Division_by_zero. I think that this might be a problem with mixing floats and ints somewhere. I have not been able to figure out how to set breakpoints for functions with ocamldebug. Can anyone identify where my mistake is happening? Thank you.

(define (factorial n)
    (if (zero? n) 1 (* n (factorial (sub1 n)))))

(define (find-e accuracy sum)
    (if (zero? accuracy) (add1 sum)
        (find-e (sub1 accuracy) (+ sum (/ 1 (factorial accuracy))))))

(display (format "~f" (find-e 100 0)))
let rec factorial n = if n == 0 then 1 else n * factorial (n - 1) ;;

let rec find_e accuracy sum =
    if accuracy == 0
        then (sum + 1)
        else find_e (accuracy - 1) (sum + (1 / factorial accuracy)) ;;

let result = find_e 100 0 ;;

Solution

  • As I recall, scheme has a "numerical tower" that tries to keep you from losing accuracy in numeric computations.

    OCaml doesn't have any fancy automatic handling of numbers. Your code is using type int, which is a fixed size integer (31 or 63 bits in the usual implementations). Thus, your expression 1 / factorial accuracy will be 0 in almost all cases and values for factorial accuracy will be unrepresentable for all but the smallest values. The value of factorial 100 will be 0 because it is a multiple of 2^63:

    # let rec fact n = if n < 2 then 1 else n * fact (n - 1);;
    val fact : int -> int = <fun>
    # fact 100;;
    - : int = 0
    

    There are no floats in your code, and hence there couldn't possibly be any mixing. But OCaml doesn't allow mixing in the first place. It's a strongly typed language where int and float are two different types.

    Here is your code converted to use floats:

    let rec factorial n =
        if n = 0.0 then 1.0 else n *. factorial (n -. 1.0) 
    
    let rec find_e accuracy sum =
        if accuracy = 0.0 then
            sum +. 1.0
        else
            find_e (accuracy -. 1.0) (sum +. 1.0 /. factorial accuracy)
    
    let result = find_e 100.0 0.0
    

    If I copy/paste this into the OCaml REPL (also known as "toplevel") I see this:

    val factorial : float -> float = <fun>
    val find_e : float -> float -> float = <fun>
    val result : float = 2.71828182845904509
    

    As a side comment, the equality comparison operator in OCaml is =. Don't use == for comparisons. It's a completely different operator:

    # 1.0 == 1.0;;
    - : bool = false