Home > Net >  Ocaml function, what is the nature of the problem?
Ocaml function, what is the nature of the problem?

Time:06-12

Currently i'm trying

I have a function to calculate the inverse sum of a number

let inverseSum n =
  let rec sI n acc =
    match n with
    | 1 -> acc
    | _ -> sI (n - 1) ((1.0 /. float n)  . acc)
  in sI n 1.0;;

For example, inverseSum 2 -> 1/2 1 = 3/2 = 1.5

I try to test the function with 2 and 5, it's okay:

inverseSum 2;;
inverseSum 5;;

inverseSum 2;;
- : float = 1.5
inverseSum 5;;
- : float = 2.28333333333333321

For the moment, no problem.

After that, I initialize a list which contains all numbers between 1 and 10000 ([1;…;10000])

let initList = List.init 10000 (fun n -> n   1);;

no problem.

I code a function so that an element of the list becomes the inverse sum of the element (e.g. [1;2;3] -> [inverseSum 1; inverseSum 2; inverseSum 3])

let rec invSumLst lst =
  match lst with
  | [] -> []
  | h::t -> (inverseSum h) :: invSumLst t;;

and I use it on the list initList:

let invInit = invSumLst initList;;

So far so good, but I start to have doubts from this stage:

I select the elements of invList strictly inferior to 5.0

let listLess5 = List.filter (fun n -> n < 5.0) invInit;;

And I realize the sum of these elements using fold_left:

let foldLess5 = List.fold_left ( .) 0.0 listLess5;;

I redo the last two steps with floats greater than or equal to 5.0

let moreEg5 = List.filter (fun n -> n >= 5.0) invInit;;
let foldMore5 = List.fold_left ( .) 0.0 moreEg5;;

Finally, I sum all the numbers of the list:

let foldInvInit = List.fold_left ( .) 0.0 invInit;;

but at the end when I try to calculate the absolute error between the numbers less than 5, those greater than 5 and all the elements of the list, the result is surprising:

Float.abs ((foldLess5  . foldMore5) -. foldInvInit);;
Printf.printf "%f\n" (Float.abs ((foldLess5  . foldMore5) -. foldInvInit));;
Printf.printf "%b\n" ((foldLess5 .foldMore5) = foldInvInit);;

returns:

let foldMore5 = List.fold_left ( .) 0.0 moreEg5;;
val foldMore5 : float = 87553.6762998474733
let foldInvInit = List.fold_left ( .) 0.0 invInit;;
val foldInvInit : float = 87885.8479664799379
Float.abs ((foldLess5  . foldMore5) -. foldInvInit);;
- : float = 1.45519152283668518e-11
Printf.printf "%f\n" (Float.abs ((foldLess5  . foldMore5) -. foldInvInit));;
0.000000
- : unit = ()
Printf.printf "%b\n" ((foldLess5 .foldMore5) = foldInvInit);;
false
- : unit = ()

it's probably a rounding problem, but I would like to know where exactly the error comes from?

Because here I am using an interpreter, so I see the error "1.45519152283668518e-11"

But if I used a compiler like ocamlpro, I would just get 0.000000 and false on the terminal and I wouldn't understand anything.

So I would just like to know if the problem comes from one of the functions of the code, or from a rounding made by the Printf.printf function which wrote the result with a "non scientific" notation.

CodePudding user response:

OCaml is showing you the actual results of the operations you performed. The difference between the two sums is caused by the finite precision of floating values.

When adding up a list of large-ish numbers, by the time you reach the end of the list the number is large enough that the lowest-order bits of the new values simply can't be represented. But when adding a list of small-ish numbers, fewer bits are lost.

A system that shows foldLess5 . foldMore5 as equal to foldInvInit is most likely lying to you for your convenience.

  • Related