【问题标题】:How does OCaml manage float addition?OCaml 如何管理浮点加法?
【发布时间】:2014-06-30 10:48:22
【问题描述】:

在Java中,如果你做0.2 + 0.01,你会得到0.21000000000000002

这是由于IEEE 754

但是,在 OCaml 中,如果您执行 0.2 +. 0.01,那么您会得到正确的结果 0.21

我认为 OCaml 也遵守 IEEE 754 的浮点数,为什么 OCaml 可以给出正确的结果而 Java 不能?

【问题讨论】:

  • 是什么让你觉得OCaml的结果和Java的结果不一样?
  • @JasonMArcher:同样的潜在问题;发帖人误解了 0.20.01 是什么。 (关于另一个问题的公认答案是可怕,但权力似乎希望将事情标记为该问题的欺骗,而不是给出一个体面的答案。)
  • @tmyklebu 这不是那个问题的重复。我在我的问题中说,我理解为什么在 Java 中它不是 0.21,而是 0.210000000000000002,我知道这是由于 IEEE 754。但我不知道为什么 OCaml 似乎不像我那样工作尝试打印0.2 +. 0.01 我在屏幕上得到0.21。下面的正确答案说的是实话,是因为OCaml的打印。如果我问为什么 Java 将0.2+0.01 打印为0.21000000000000002,那么这将是一个重复。请不要滥用close 按钮的功能,因为它会误导未来有类似问题的其他人。
  • @tmyklebu 如果我问为什么 Java 或任何其他语言将 0.21000000000002 提供给 0.2 + 0.01,那么是的,这将是重复的。但实际上我是在向0.2+.0.01 询问Why **ocaml** gives 0.21`。 不是关于IEEE 754浮点如何工作之类的问题;相反,它更像是 如何打印浮动 问题。你不能说所有关于花车的问题都是重复的吧?
  • @JacksonTale:您的问题与我要链接的问题 (stackoverflow.com/questions/21895756/…) 没有根本不同。我链接的问题只有废话答案,对此我深表歉意。 (现在,如果测试是“关闭垃圾并且不关闭非垃圾”,我可能不会关闭这个。但事实并非如此;它是“VTC-as-duplicate questions 本质上是相同的问题。” )

标签: floating-point ocaml


【解决方案1】:

在这种情况下,哪一个是“正确的”?从浮点运算的角度来看,Java 在这里是正确的。总之,

OCaml 顶层的值由genprintval.ml 打印,float 的值由print_float 打印,它使用string_of_float。它的定义在pervasives.ml:

let string_of_float f = valid_float_lexem (format_float "%.12g" f)

正如您在此处看到的,浮点数是使用 printf 格式“%.12g”打印的。小于 10^{-12} 的东西会被简单地丢弃。这就是您看到“不正确”答案0.21 的原因。如果提高精度,则输出与 Java 相同:

# Printf.sprintf "%.20g" (0.2 +. 0.01);;
- : string = "0.21000000000000002"

【讨论】:

    【解决方案2】:

    OCaml 对于它调用 float 的类型,使用底层 C/Unix 平台的 double 类型,该平台通常将其定义为 IEEE 754 的 binary64 格式。

    在OCaml中,转换成十进制是用老式的方式完成的,位数是固定的(camlspotter已经挖出了格式,也就是%.12g,在OCaml中和这种格式的含义是一样的在 C) 中。

    在现代语言(Java、Javascript、Ruby)中,流行的方式是通过发出与十进制表示所需的位数完全相同的数字来转换为十进制,以在另一个方向转换回原始浮点数。因此,在 Java 中,0.21 仅针对最接近 0.21 的 double 打印,这不是有理数 21/100,因为该数字不能完全表示为二进制浮点数。

    一种方法并不比另一种更好。对于未经警告的开发人员来说,它们都有令人惊讶的副作用。尤其是Java的转换方式,导致很多“为什么我的float转换成double的值会变?” StackOverflow 上的questions(答案:它没有,但(double)0.1f0.100000 之后打印了许多额外的数字,因为double 类型包含的值比float 多)。

    无论如何,OCaml 和 Java 都为 0.2 + 0.01 计算相同的浮点数,因为它们都严格遵循 IEEE 754。它们只是打印它们的方式不同。 OCaml 打印固定数量的数字,但不足以表明该数字既不是 21/100,也不是最接近 21/100 的双精度浮点表示。 Java 打印了足够多的数字以表明该数字不是最接近 21/100。

    【讨论】:

      【解决方案3】:

      我猜你是通过打印浮点数看到的,但除非你使用像this one 这样的无损表示,否则永远不要依赖它来推断浮点计算的属性。宁可这样做:

      # 0.21 = 0.2 +. 0.01;;
      - : bool = false
      # 0.21000000000000002 = 0.2 +. 0.01;;
      - : bool = true
      

      【讨论】:

        猜你喜欢
        • 2015-02-18
        • 2013-07-09
        • 2011-12-24
        • 2020-01-26
        • 1970-01-01
        • 2013-04-18
        • 2017-03-13
        • 1970-01-01
        • 2012-11-08
        相关资源
        最近更新 更多