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.