在Java中,如果您执行0.2 + 0.01
,则会获得0.21000000000000002
这是由于 IEEE 754 。
但是,在OCaml中,如果您执行0.2 +. 0.01
,则会得到正确的结果0.21
。
我认为OCaml也服从 IEEE 754 for floats,为什么OCaml可以提供正确的结果,而Java却不能?
答案 0 :(得分:10)
哪一个是#34;正确"在这种情况下?从浮点算术的角度来看,Java在这里是正确的。无论如何,
OCaml toplevel中的值由genprintval.ml
打印,float
值由print_float
打印,string_of_float
使用pervasives.ml
。其定义在let string_of_float f = valid_float_lexem (format_float "%.12g" f)
:
0.21
正如您在此处看到的那样,使用printf格式打印浮动"%。12g"。小于10 ^ { - 12}的东西被简单地丢弃。这就是为什么你看到"错误"回答# Printf.sprintf "%.20g" (0.2 +. 0.01);;
- : string = "0.21000000000000002"
。如果升高精度,则输出与Java相同:
{{1}}
答案 1 :(得分:6)
OCaml,对于它所调用的类型float
,使用底层C / Unix平台的double
类型,该平台通常由该平台定义为IEEE 754的binary64格式。
在OCaml中,转换为十进制是以老式的方式完成的,具有固定的位数(camlspotter已经挖出了格式%.12g
,在OCaml中的含义与此相同格式有C)。
在现代语言(Java,Javascript,Ruby)中,时尚是通过精确地发送十进制表示所需的数字来转换为十进制,如果转换回另一个方向,则转换回原始浮点数。所以在Java 0.21
中打印的double
仅用于最接近0.21的float
,这不是理性的21/100,因为这个数字不能完全表示为二进制浮点数。
一种方法并不比另一种方法好。他们都对未经警告的开发人员产生了令人惊讶的副作用。特别是,Java转换方法导致了许多“为什么我的double
的值在我将其转换为(double)0.1f
?questions时会在StackOverflow上发生变化(答案:它没有&#39} ; t,但0.100000
在double
之后打印了许多其他数字,因为类型float
包含的值多于0.2 + 0.01
。)
无论如何,OCaml和Java都为{{1}}计算相同的浮点数,因为它们都严格遵循IEEE 754.它们只是以不同的方式打印它们。 OCaml打印固定数量的数字,这些数字不足以显示该数字既不是21/100,也不是最接近21/100的双精度浮点表示。 Java打印足够的数字以显示该数字不是最接近21/100。
答案 2 :(得分:2)
我猜您是通过打印浮点数来查看的,但除非您使用像this one之类的无损表示,否则永远不会依赖它来推断浮点计算的属性。相反:
# 0.21 = 0.2 +. 0.01;;
- : bool = false
# 0.21000000000000002 = 0.2 +. 0.01;;
- : bool = true