OCaml 有类 C 的 round() 和 trunc() 函数吗?
Does OCaml have C-like round() and trunc() functions?
OCaml 的标准库包括几个等价于 C 的浮点函数,例如 mod_float
用于 C 的 fmod()
,求幂运算符 **
用于 C 的 pow()
,以及其他函数,例如 ceil
、log
等
但是它是否也包括 round()
和 trunc()
的等价物?有 truncate
/int_of_float
,但它们的类型是 float -> int
而不是 float -> float
。
如果你想要一个类型为 float -> float
的函数 truncate,你可以这样做,但它非常难看:
let mytruncate x = float_of_int (truncate x)
Core
库有一个包含许多此类函数的 Float 模块。
它包含modf
函数,即瑞士刀函数,您可以使用它定义truncatef
和roundf
函数:
# let truncatef x = snd (modf x);;
val truncatef : float -> float = <fun>
# truncatef 3.14;;
- : float = 3.
round
函数也可以用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.
但是可以用 floor
更简洁(和有效)地表达
# let roundf x = floor (x +. 0.5)
但是这两个舍入函数都有点问题,正如 core
实现中的评论所述:
(* 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.
所以实现舍入的更正确的方法是(来自核心库):
let round_nearest t =
if t >= round_nearest_lb && t <= round_nearest_ub then
floor (t +. 0.5)
else
t
但即使这样round_nearest
也不是完美无缺的,例如:
# round_nearest 0.49999999999999994;;
- : float = 1.
这个 0.49999999999999994
是 0.5
的前身。 Pascal 的 blog 包含有关如何解决此问题的建议。以下应该在 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.
#
这只是一种舍入策略,四舍五入到最近(直观的一种)。还有其他政策,它们有自己的警告。
关于截断,已经有人给你答案了:
let truncatef x = snd (modf x);
对于一轮,我建议:
BatFloat.round_to_int
或 BatFloat.round
查看 this 了解详情。
OCaml 的标准库包括几个等价于 C 的浮点函数,例如 mod_float
用于 C 的 fmod()
,求幂运算符 **
用于 C 的 pow()
,以及其他函数,例如 ceil
、log
等
但是它是否也包括 round()
和 trunc()
的等价物?有 truncate
/int_of_float
,但它们的类型是 float -> int
而不是 float -> float
。
如果你想要一个类型为 float -> float
的函数 truncate,你可以这样做,但它非常难看:
let mytruncate x = float_of_int (truncate x)
Core
库有一个包含许多此类函数的 Float 模块。
它包含modf
函数,即瑞士刀函数,您可以使用它定义truncatef
和roundf
函数:
# let truncatef x = snd (modf x);;
val truncatef : float -> float = <fun>
# truncatef 3.14;;
- : float = 3.
round
函数也可以用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.
但是可以用 floor
# let roundf x = floor (x +. 0.5)
但是这两个舍入函数都有点问题,正如 core
实现中的评论所述:
(* 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.
所以实现舍入的更正确的方法是(来自核心库):
let round_nearest t =
if t >= round_nearest_lb && t <= round_nearest_ub then
floor (t +. 0.5)
else
t
但即使这样round_nearest
也不是完美无缺的,例如:
# round_nearest 0.49999999999999994;;
- : float = 1.
这个 0.49999999999999994
是 0.5
的前身。 Pascal 的 blog 包含有关如何解决此问题的建议。以下应该在 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.
#
这只是一种舍入策略,四舍五入到最近(直观的一种)。还有其他政策,它们有自己的警告。
关于截断,已经有人给你答案了:
let truncatef x = snd (modf x);
对于一轮,我建议:
BatFloat.round_to_int
或 BatFloat.round
查看 this 了解详情。