Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 744
anol Avatar asked Jun 09 '15 12:06

anol


3 Answers

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.

like image 171
ivg Avatar answered Nov 04 '22 15:11

ivg


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

like image 4
Ashish Agarwal Avatar answered Nov 04 '22 14:11

Ashish Agarwal


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.

like image 1
ligand Avatar answered Nov 04 '22 15:11

ligand