Does OCaml have C-like round() and trunc() functions?

2.6k views Asked by At

OCaml's standard library includes several floating-point functions equivalent to C ones, such as mod_float for C's fmod(), the exponentiation operator ** for C's pow(), and other functions such as ceil, log, etc.

But does it also include equivalents for round() and trunc()? There is truncate/int_of_float, but their type is float -> int and not float -> float.

4

There are 4 answers

2
ivg On BEST ANSWER

It contains the modf function, that is a swiss-knife function, with which you can define the truncatef and roundf functions:

# let truncatef x = snd (modf x);;
val truncatef : float -> float = <fun>
# truncatef 3.14;;
 - : float = 3.

The round function also can be expressed with modf

# let roundf x = snd (modf (x +. copysign 0.5 x));;
val roundf : float -> float = <fun>
# roundf 3.14;;
- : float = 3.
# roundf 3.54;;
- : float = 4.
# roundf (~-.3.54);;
- : float = -4.

However it can be expressed more succinctly (and efficiently) with floor

# let roundf x = floor (x +. 0.5)

But both rounding functions are a little bit buggy, as comment in core's implementation states:

(* Outside of the range [round_nearest_lb..round_nearest_ub], all representable doubles
   are integers in the mathematical sense, and [round_nearest] should be identity.

   However, for odd numbers with the absolute value between 2**52 and 2**53, the formula
   [round_nearest x = floor (x + 0.5)] does not hold:

   # let naive_round_nearest x = floor (x +. 0.5);;
   # let x = 2. ** 52. +. 1.;;
   val x : float = 4503599627370497.
   # naive_round_nearest x;;
   - :     float = 4503599627370498.
*)
let round_nearest_lb = -.(2. ** 52.)
let round_nearest_ub =    2. ** 52.

So a little bit more correct way to implement the rounding would be (from Core library):

let round_nearest t =
  if t >= round_nearest_lb && t <= round_nearest_ub then
    floor (t +. 0.5)
  else
    t

But even this round_nearest is not flawless, for example:

# round_nearest 0.49999999999999994;;
- : float = 1.

This 0.49999999999999994 is an immediate predecessor of 0.5. Pascal's blog contains suggestions on how to solve this issue. The following should work in OCaml:

let round_nearest t =
  if t >= round_nearest_lb && t <= round_nearest_ub then
    floor (t +. 0.49999999999999994)
  else
    t

# round_nearest 0.49999999999999994;;
- : float = 0.
# round_nearest (~-.0.49999999999999994);;
- : float = 0.
# round_nearest (~-.1.49999999999999994);;
- : float = -1.
# round_nearest 0.5;;
- : float = 1.
# round_nearest ~-.0.5;;
- : float = -1.
# 

And this is only one rounding policy, round to nearest (the intuitive one). There are other policies, that have their own caveats.

1
Ashish Agarwal On

The Core library has a Float module with many such functions.

2
alifirat On

If you want a function truncate having the type float -> float, you can do this but it is very ugly :

let mytruncate x = float_of_int (truncate x)
0
ligand On

For truncate, someone gave you an answer already:

let truncatef x = snd (modf x);

For round, I suggest: BatFloat.round_to_int or BatFloat.round

Check this for details.