18

I have read these:

They explain "how". I'd like to know why it's different across these languages. I expected similar results given the same input.

test.js

#!/usr/bin/env node

var nine = 9.0;
var pointOhOhOne = 0.001;
var result = nine * pointOhOhOne;
console.log(result);

test.java

public class test {

  public static void main(String[] argv) {
    double nine = 9.0d;
    double pointOhOhOne = 0.001d;
    double result = nine * pointOhOhOne;
    System.out.println(result);
  }

}

test.c

#include "stdio.h"

int main() {
  double nine = 9.0;
  double pointOhOhOne = 0.001;
  double result = nine * pointOhOhOne;
  printf("%f", result);
}

test.rb

#!/usr/bin/env ruby

nine = 9.0
pointOhOhOne = 0.001
result = nine * pointOhOhOne

print result

test.py

#!/usr/bin/env python

nine = 9.0
pointOhOhOne = 0.001
result = nine * pointOhOhOne

print result

results:

ruby     0.009000000000000001
python   0.009
node     0.009000000000000001
java     0.009000000000000001
c        0.009000

gist: https://gist.github.com/reklis/6694ad5fb01991a79a1a

Community
  • 1
  • 1
slf
  • 22,595
  • 11
  • 77
  • 101
  • 19
    Perhaps the different languages all _do_ get the same answer, but the way they display it is different. For instance, you can get more printed accuracy in Python using the `decimal` module. `import decimal; print decimal.Decimal(9.0 * 0.001)` gives `0.009000000000000001054711873393898713402450084686279296875`. – Kevin Oct 02 '14 at 20:20
  • 2
    Since you don't actually test whether or not any of the values are *equal* to anything in particular, is your question is really about why the *representations* are different? – DSM Oct 02 '14 at 20:20
  • 3
    It looks like C/C+ by default [prints 6 significant digits](http://stackoverflow.com/questions/20135901/c-cout-double-not-printing-decimal-places). – rgettman Oct 02 '14 at 20:21
  • 2
    @rgettman: Those 6 decimal places are guaranteed in C, and by reference in C++. – Deduplicator Oct 02 '14 at 20:31
  • all these languages use floating point arithmetic by default; but there are languages with built-in rational types, and these can represent this calculation exactly – Christoph Oct 02 '14 at 21:09
  • The full precision Java result is also 0.009000000000000001054711873393898713402450084686279296875 – Patricia Shanahan Oct 03 '14 at 00:40
  • 3
    Meta comment here: does anyone think it would be more useful to have the [tag:floating-point] or [tag:floating-point-precision] tags here than all 5 languages? I'm trying to think of what will make this come up in search results. – Chris Hayes Oct 03 '14 at 05:34
  • @chrishayes good suggestion. I adjusted the title and tag to be more accurate – slf Oct 03 '14 at 11:40
  • See also this question: http://stackoverflow.com/questions/21872854 – Mark Dickinson Oct 03 '14 at 13:45
  • The point of this question is to act as a replacement for "is floating point math broken?", right? What can we do to make it appear easily in searches? – tmyklebu Oct 03 '14 at 17:46

2 Answers2

17

In C on my system:

printf("%.18f\n", result);

0.009000000000000001

In Python on my system:

print("%.18f" % result)

0.009000000000000001

C or Python like other languages limit the number of decimal digits by default with their print functions.

ouah
  • 142,963
  • 15
  • 272
  • 331
16

@ouah establishes that the languages are all behaving the same. My answer aims to explain why they appear different. The only two languages that have "different" output are C and Python.

Clearly, every language besides C and Python is just printing out the float value to as many decimal places as it can.

C is easy to explain. You use printf("%f", result), without specifying an explicit precision value. Per the C standard, the precision of the f specifier defaults to 6. Thus, exactly six decimal places are printed out, which is what you see. As @ouah notes, setting the precision to 18 will yield the "expected" output. This is lossy: doubles that differ past the 7th decimal place will be printed out identically, and so the output of %f cannot be relied on to exactly reconstruct the original float.

Python is a bit trickier. Python 3.1 introduced a new floating-point repr algorithm, based on work by David Gay. The Python issue corresponding to the feature is here: http://bugs.python.org/issue1580. This feature was backported to Python 2.7 as well.

The intention of this new feature was to both reduce confusion over floating point (though that is dubiously useful), and more importantly to provide more human-readable, shorter representations of floating point numbers without affecting round-trip behaviour; that is, float(repr(x)) is always equal to x, even if repr(x) is shortened due to this algorithm. So, the algorithm manages to produce a shorter floating-point representation while remaining "lossless": win-win!

The official description says this much:

The new algorithm for repr(1.1) is smarter and returns '1.1'. Effectively, it searches all equivalent string representations (ones that get stored with the same underlying float value) and returns the shortest representation.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • 2
    You wrote "every language besides C and Python is just printing out the float value to as many decimal places as it can" but that's really not true. Java and Javascript, for example, print out as many decimal places as they need to, to distinguish this particular double-precision number from those on either side. I'm not an expert on Ruby, but it appears to be doing the same thing. Of course, any one of these languages could print out many more decimal places - as per Kevin's comment under the question. – Dawood ibn Kareem Oct 03 '14 at 00:31
  • That Python behavior is very strange... and intriguing. I'd like to have that as an available tool, though not as the default. – Mooing Duck Oct 03 '14 at 00:35
  • 1
    @MooingDuck: Python's behaviour is not all that unusual: Java's `Double.toString` does essentially the same thing, and Tcl and (I believe) Ruby >= 1.9 both do something similar. – Mark Dickinson Oct 03 '14 at 15:53
  • @MarkDickinson +1, and also add scheme, Squeak and Pharo Smalltalk among others. – aka.nice Oct 04 '14 at 20:43