2

In Java, if you do 0.2 + 0.01, you will get 0.21000000000000002

This is due to IEEE 754.

However, in OCaml, if you do 0.2 +. 0.01, then you get the correct result 0.21.

I think OCaml also obeys IEEE 754 for floats, why OCaml can give correct result while Java cannot?

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
Jackson Tale
  • 25,428
  • 34
  • 149
  • 271
  • What makes you think that the result of OCaml is different from the result of Java? – Pascal Cuoq Jun 30 '14 at 11:32
  • No, I meant that you only see the decimal representation that each language displays **by default** for a binary64 number that could be the same in both languages. The floating-point number is not its decimal representation. There are several conventions to go from one to the other, and they give differing results. Anyway I have expanded my comment in an answer. – Pascal Cuoq Jun 30 '14 at 11:46
  • possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – tmyklebu Jun 30 '14 at 18:29
  • @tmyklebu Not a dupe, this question is about differences between OCaml and Java. – JasonMArcher Jun 30 '14 at 21:54
  • 1
    @JasonMArcher: Same underlying problem; the poster misunderstands what `0.2` and `0.01` are. (The accepted answer on the other question is *horrible*, but the powers that be seem to want things to be marked as a dupe of that question rather than given a decent answer.) – tmyklebu Jun 30 '14 at 22:18
  • 1
    @tmyklebu this is not a dup of that question. I said in my question that I understand why in Java it is not `0.21`, but `0.210000000000000002` and I know it is due to IEEE 754. But I didn't know why OCaml doesn't seem to work like that as I tried printing `0.2 +. 0.01` I get `0.21` on screen. The correct answer below told the truth, it is because of the printing of OCaml. If i were asking about why Java prints `0.2+0.01` as `0.21000000000000002`, then it would be a dup. Please don't misuse the power of the `close` button as it will mislead future others who have similar questions. – Jackson Tale Jul 01 '14 at 08:42
  • I don't believe I'm misusing the close button. My takeaway from http://meta.stackoverflow.com/questions/260130/canonical-duplicate-for-floating-point-is-inaccurate was that, regardless of content, SO moderators would prefer that questions like yours were marked duplicates of one specific earlier question (I got the wrong one; I should have pointed you at the Python one) rather than attracting useful, specific answers. – tmyklebu Jul 01 '14 at 14:36
  • 2
    @tmyklebu If I asked *Why Java or any other language gives `0.21000000000002` to `0.2 + 0.01`?*, then yes, it would be a duplicate. But actually I was asking `Why **ocaml** gives `0.21` to `0.2+.0.01`. It is **not** about *how IEEE 754 floating point works* sort of questions; instead, it is more like *how is float printed* question. You can't say all questions about floats are duplicates right? – Jackson Tale Jul 01 '14 at 15:01
  • 1
    @JacksonTale: Your question isn't fundamentally different from the one I meant to link (http://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate). The question I did link only has crap answers, and I'm sorry for that. (Now, if the test were "close crap and don't close non-crap", I probably wouldn't have closed this. But it's not; it's "VTC-as-duplicate questions that are asking fundamentally the same thing.") – tmyklebu Jul 01 '14 at 16:02

3 Answers3

10

Which one is "correct" in this case? From the view of the floating point arithmetics, Java is correct here. Anyway,

The values in OCaml toplevel is printed by genprintval.ml and there float values are printed by print_float, which uses string_of_float. Its definition is in pervasives.ml:

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

As you see here the floats are printed using the printf format "%.12g". Things smaller than 10^{-12} are simply discarded. That's why you see the "incorrect" answer 0.21. If you upsize the precision, you have the same output as Java:

# Printf.sprintf "%.20g" (0.2 +. 0.01);;
- : string = "0.21000000000000002"
camlspotter
  • 8,990
  • 23
  • 27
6

OCaml, for the type that it calls float, uses the double type of the underlying C/Unix platform, which is usually defined by that platform as IEEE 754's binary64 format.

In OCaml, the conversion to decimal is done in the old-fashioned way, with a fixed number of digits (camlspotter has already dug up the format, which is %.12g, with the same meaning in OCaml that this format has in C).

Among modern languages (Java, Javascript, Ruby), the fashion is to convert to decimal by emitting exactly as many digits required for the decimal representation to convert back to the original floating-point number if converted back in the other direction. So in Java 0.21 is printed for and only for the double nearest to 0.21, which is not the rational 21/100 as this number is not exactly representable as a binary floating-point number.

One method is not better than the other. They both have surprising side-effects for the unwarned developer. In particular, the Java conversion method has lead to many “Why does the value of my float change when I convert it to double?” questions on StackOverflow (Answer: it doesn't, but (double)0.1f is printed with many additional digits after 0.100000 because the type double contains more values than float).

Anyway, both OCaml and Java compute the same floating-point number for 0.2 + 0.01, because they both closely follow IEEE 754. They just print them differently. OCaml prints a fixed number of digits that does not go far enough to show that the number is neither 21/100 nor the double-precision floating-point representation closest to 21/100. Java prints enough digits to show that the number is not the closest to 21/100.

Community
  • 1
  • 1
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
2

I guess you looked that by printing the floating point number, but except if you use a lossless representation like this one never rely on that to infer properties of your floating point calculations. Rather do:

# 0.21 = 0.2 +. 0.01;;
- : bool = false
# 0.21000000000000002 = 0.2 +. 0.01;;
- : bool = true
Daniel Bünzli
  • 5,119
  • 20
  • 21