253 comments

[ 4.3 ms ] story [ 113 ms ] thread
This table does not show which languages have support for Decimal numbers, which does the calculation exactly.
Basically all languages have a Decimal number library you can use if you need it.
Can it calculate 1/3 exactly? You can in trinary: It's 0.1.

Binary, decimal, trinary, or any other numbering system is no more accurate or "exact" than any other. They all have fractions that they cannot represent.

For that you'd need a Rational.
"Computers can only natively store integers, so they need some way of representing decimal numbers"

Technically, [digital] computers only natively "store" high and low voltages. The interpretation of a sequence of those signals as "integers" is entirely up to you.

In all seriousness, representing integers is not a given either (e.g. two's complement vs one's complement, BCD, bignum, sparse integers, etc.). If you are going down in abstraction levels to describe floating point representation, you should not just call integers "native" blindly.

Furthermore, floating point is not the only way to represent computation involving real values. It is indeed possible to represent computation on real values symbolically in many cases and lazily compute arbitrary precision results for output-printing purposes.

Technically, much storage does not need voltage being high or low either. Pits burned with lasers, and magnetic changes are also common ways to store stuff that don't involve voltage in the stored state.

Here you can see python has a fractions.Fraction type which can be used for rational number arithmetic.

  >>> from fractions import Fraction
  >>> Fraction("0.1") + Fraction("0.2")
  Fraction(3, 10)

  >>> repr(float(Fraction("0.1") + Fraction("0.2")))
  '0.3'
Here we see the floating point behaviour.

  >>> 0.1 + 0.2
  0.30000000000000004
Here we see that python has a nice behaviour for when printing floats.

  >>> print(0.1 + 0.2)
  0.3
 
Here is a common error where people feed the input of Fraction (and Decimal) with a float. Even though you use the Fraction here it still carries the floating point error through.

  >>> Fraction(0.1) + Fraction(0.2)
  Fraction(10808639105689191, 36028797018963968)
  >>> float(Fraction(0.1) + Fraction(0.2))
  0.30000000000000004

Here is the Decimal type which does not have this float problem. Some people have argued (including myself) that Decimal should be used by python instead of float. Since this is much friendlier to people doing things like adding these numbers together.

  >>> from decimal import Decimal
  >>> Decimal("0.1") + Decimal("0.2")
  Decimal('0.3')
  >>> float(Decimal("0.1") + Decimal("0.2"))
  0.3
Here is the Decimal type which does not have this float problem.

Are you sure?

  >>> Decimal(0.1)
  Decimal('0.1000000000000000055511151231257827021181583404541015625')
use Decimal('0.1'), otherwise you will first generate a floating point number (with accuracy loss) and the convert it to a decimal.
It does not if you pass the string representation of the float you're working on. Like BigDecimal in Java.
This is a good case for macros in Python, actually. Fraction('0.1') is usually what you want, so Fraction(0.1) should mean just that and if you do want the current behaviour, Fraction(float(0.1)) would do it. Same for Fraction(17/3) for example.
> Technically, [digital] computers only natively "store" high and low voltages. The interpretation of a sequence of those signals as "integers" is entirely up to you.

And then there's MLC NAND flash which stores bit sequences as different voltage levels within the same cell.

I think the words we're really looking for are "discrete" vs "continuous". Any discrete values will be bounded by some sort of precision limit.
That depends on the number you want to represent. Some values can be represented exactly, others with varying degrees of precision because of the difference between what is actually stored and what you wanted to store. This will collapse a number of values into the same precision 'bucket'.
What is continuous or precise depends on the base you use. I can't think of any example in base 2, but I believe in base 3 you could represent numbers that have infinite digits in base 10.

If I'm not mistaken, 0.3333... would be 0.1 and 0.6666... would be 0.2. It probably gets nasty for a lot of simple base 10 numbers, so it isn't a good solution.

Right you are. One half is 0.5 in decimal or 0.1 in binary but 0.11111... in base 3
That's valid, but I was going for an ELI5 style explanation of a concept that surprised and confused me at the time. If you want to provide a better one, I'm happy to merge any pull requests.
when you aim for reddit style stuff like "ELI5", don't be surprised if HN tears you up. reddit likes things to be cool and cliquey. we like things to be correct.

also we don't have 5 year olds.

FWIW, tried this with two less common languages:

    Tcl: + 0.1 0.2 
      0.30000000000000004

    Chicken Scheme: (+ 0.1 0.2)
      0.3
Same results under Windows 8.1 and FreeBSD 10.2. I suppose one is as "right" as the other, at least both answers equally popular among languages.
Another less-common language. The docs suggests they use "C doubles" for all floats.

    Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 6.6.6)

    ?- Sum is 0.1 + 0.2.
    Sum = 0.30000000000000004.
Also Chicken Scheme:

  (- 0.3 (+ 0.1 0.2))
    -5.55111512312578e-17
Of course you can always (use numbers), but then you have to use (/ 1 20) and (/ 2 10). "0.3" is still considered "inexact", because of the decimal point. I don't like this - finite decimals are perfectly rational numbers and trivially convertible, why can't the interpreter treat them as such?
What are the differences in implementation of the languages that cause some to show a rounding error and others not?
I suspect in many instances it's down to the method doing the printing being "helpful". The page documents this behaviour with Python. I've never specifically used C#, but it seems to default to the "G" format[1], the alternative "R" format shows the expected result. You can play around with it here: http://ideone.com/hxD96J

Printing out is kind of a misleading check here, maybe a better idea would be to test equality with the constant 0.3. Of course the site is just a neat illustration and not a technical whitepaper.

[1] https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).... and https://msdn.microsoft.com/en-us/library/3hfd35ad(v=vs.110)....

Yes, I agree this page is misleading. It makes it look like C# is using rational numbers instead of floating point. In fact the "G" format defaults to 15 digits of precision for doubles, which is few enough to avoid ugly strings in many simple situations, but not all:

> Console.WriteLine((.1 + .2)-.3); > Result: 5.55111512312578E-17

The printing routine. Internally, none of them can store 0.3 in a floating point variable because it is impossible to do so in exponential notation with base 2 (which is what IEE 754 uses).

Some languages may optionally use an exact representation (e.g., Scheme or Python), but that's not the general case.

What Every Computer Scientist Should Know About Floating-Point Arithmetic: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.h...
Here is the part that made me understand it all: "Floating-point representations have a base (which is always assumed to be even) and a precision p. If = 10 and p = 3, then the number 0.1 is represented as 1.00 × 10-1. If = 2 and p = 24, then the decimal number 0.1 cannot be represented exactly, but is approximately 1.10011001100110011001101 × 2-4."
Python's print method is actually fairly clever picking the "nicest" representation that's still correct.

https://docs.python.org/2/tutorial/floatingpoint.html#tut-fp...

Quote:

"In current versions, Python displays a value based on the shortest decimal fraction that rounds correctly back to the true binary value, resulting simply in ‘0.1’."

Additionally, for exact calculations you might want to use the decimal module. https://docs.python.org/3.5/library/decimal.html#module-deci...
But be wary so you don't pass in floats when creating your decimals

    In [10]: from decimal import Decimal
    In [11]: Decimal(0.1) + Decimal(0.2) - Decimal(0.3)
    Out[11]: Decimal('2.775557561565156540423631668E-17')   

    In [12]: Decimal("0.1") + Decimal("0.2") - Decimal("0.3")
    Out[12]: Decimal('0.0')
Try the usual decimally-unrepresentable numbers (like 1/3).
As illumen pointed out, Python has also got fractions. For things like Pi however, there is no exact numerical representation and you'd probably want sympy or something similar.
Sure. But the inability to represent 1/3 in decimal is just the same as inability to represent 1/10 in binary. The binary case just seems different because we are using a foreign base (decimal) literal "0.1" to express the binary number.
This doesn't seem true for me (Python 3.5.0 (default, Sep 20 2015, 11:28:25) [GCC 5.2.0] on linux).

    >>> print(.1 + .2)
    0.30000000000000004
(comment deleted)
I'm not sure what was the intended message of the table of examples. It is possible in many languages to get both types of ASCII representations for floating point: a nice human-readable 0.3 and the technically correct 0.30000000000000004. But some examples seem artificially biased towards one or the other. Only for Python both variants are shown. And then some examples use decimal representations, which are not floating point at all.

For instance, in Perl "print 0.1+0.2" will give you "0.3" just like in Python, while the printf format shown here will give you the non-rounded representation. And there are of-course decimal and rational number packages for Perl as well.

Yep, it's better to print the result of 0.1+0.2-0.3
Not generally!

    $ perl -E 'say sprintf "%.64f", 0.1 + 0.2'
    0.3000000000000000000108420217248550443400745280086994171142578125
    $ perl -E 'say sprintf "%.64f", 0.1 + 0.2 - 0.3'
    0.0000000000000000000000000000000000000000000000000000000000000000
My Perl is configured with -Dusemorebits. https://metacpan.org/pod/distribution/perl/INSTALL#more-bits
This is more a comparison of output routines than anything else. In PHP, one of the languages that gives the "correct" result:

    php > echo .1 + .2;
    0.3
    php > if (.1 + .2 === .3) { echo "equal"; } else { echo "not equal"; }
    not equal
Errr that's exactly what the page implies; it does not say nor imply "those that display .3 are doing it correctly internaly".

One of the cardinal rule of floating point in computing is that you never do direct comparison between values, whatever the language, so your test should never ever be used. That doesn't mean a language cannot be allowed to infer what the correct display of a floating point value should be.

(comment deleted)
Just adding 0.1 to 0.2 in the language of your choice can be misleading because printing the number might just cut off the digits at some point.

Here's an example using perl5:

perl -e 'print 0.1+0.2' yields '0.3', which looks OK, however

perl -e 'print 0.1+0.2-0.3' reveals '5.55111512312578e-17'

So, it's better to print the result of 0.1+0.2-0.3

For what its worth, perl6 passes this test with flying colours.

> For what its worth, perl6 passes this test with flying colours.

Do we really expect calculations on floating point numbers to be the same as calcualtions with real numbers?

The only problem I see is that often the floating point format is not specified properly. When one writes "0.3", what does it actually mean? Does the format the compiler uses internally conform to IEEE 754? Is it 32bit, 64bit, 80bit or something else?

In other words, there are about 5648798746465 possible interpretations of the "(\+|-|)[0-9]+.[0-9]+" format, and the problem is with mismatched assumptions ("of course I'm using the One Correct Interpretation, like everyone else!").
The only problem I see is that often the floating point format is not specified properly. When one writes "0.3", what does it actually mean?

It's perfectly well-specified. If you are using double-precision floating point, "0.3" means 0x3fd3333333333333 (as bits), or 0.299999999999999988897769753748434595763683319091796875 (as an exact number: the closest representable number to 0.3).

When your compiler does its internal calculations in double precision float you are right.

But why should it do so? Do all languages specify the details of how floating point input is handled by the compiler/interpreter?

I think so, yes.
Most languages do (though implementation bugs are common—plenty of languages where 64-bit precision is required actually end up being implemented in such a way that 80-bit precision is used on x86).

That said, a lot of low-level languages don't: C doesn't guarantee this, for example. C doesn't even mandate IEEE 754, and it merely requires that "float" is a subset of the values of "double" which is itself a subset of the values of "long double".

Perl 6 doesn't use IEEE 754 by default at all. It internally stores decimal literals as rational numbers, with numerator and denominator in separate fields, and it only resolves back to a decimal number when you convert it to a string.
Those 0.1, 0.2, and 0.3 numbers do not have any floating point representation at all. Computers just can not use them.
But these numbers are perfectly computable! Sure they cannot be represented in the commonly used binary implementation of floating point numbers, but that doesn't mean computers cannot operate with these numbers at all.
Well, they can't be specified as IEEE 754 floats. A computer can still use those numbers in the form of decimal fixed-point or numerator-denominator formats.
Of course they do, just not a binary floating point representation. They can be represented with decimal floating point just fine and there are plenty of programs and libraries that can do this.

It's very commonly used in finance, for example.

I thought finance worked using integers internally for this exact reason.
Maybe for accounts bookkeeping, but I doubt it is feasible, or even desirable, for financial statistical work.
Accounting systems I've worked with do most of their logic in a RDBM and store monetary types using a 'decimal' type.
There are several ways to do it.

One it so use fixed point decimal with a 1/100 scaling instead of floating point. So yes, integers and then you shift the decimal point when you output.

But you have to watch out for interest calculations, you might earn a fraction of a penny on a daily basis but over a year it adds up and you can't throw away that accuracy.

So you might do all math as floating point, but with pennies instead of dollars.

Or you can do Binary Coded Decimal, which stores each group of decimal number after the decimal point exactly, in many ways it similar to fixed point decimal, since it is a fixed point.

Do they do USD interest calculations in something smaller than 1/10000 (decimill I think)?

The issue with BCD would be data store support and CPU support right? Do CPUs support doing math directly with BCD values and does your DB support doing math directly with BCD values?

In any case, using floating point for any currency calculations is a huge no-no, even though a lot of things do it anyway.

I work with Magento daily and all their currency math uses PHP floats and the rounding errors drive me bonkers.

"Finance" uses lots of different things. Sometimes it uses symbolic math; sometimes it uses binary floating point, sometimes it uses decimal floating point, sometimes it uses arbitrary precision decimals or rationals, sometimes it uses integers in the smallest unit of concern for the application.

Finance is a big domain.

> printing the number might just cut off the digits at some point.

I'm not sure, but I think it's not really about cutting off the digits (although that's the effect it has). At least with python, I think what happens is:

* repr gives you the decimal number with the exact value of the floating-point number, i.e. 0.30000000000000004

* print gives you the least-precise decimal number whose closest floating-point representation is equal to the floating-point number

That is, there are (infinitely) many decimal numbers such that the closest floating-point representation has the value 0.30000000000000004. One of these decimal numbers is 0.3, and of all such decimal numbers, this has the fewest digits. So that's the one that gets printed.

(comment deleted)
Someone pointed out that this isn't correct, and then deleted their comment. (?)

I now think the behiviour I ascribed to print, is actually what repr does. I'm not sure about print, but I guess it is just rounding to a certain number of places.

    >>> from decimal import Decimal

    >>> Decimal(0.1+0.2)
    Decimal('0.3000000000000000444089209850062616169452667236328125')

    >>> Decimal(0.3)
    Decimal('0.299999999999999988897769753748434595763683319091796875')

    >>> Decimal(0.300000000000000000000000000001)
    Decimal('0.299999999999999988897769753748434595763683319091796875')

    >>> print 0.300000000000000000000000000001
    0.3

    >>> repr(0.300000000000000000000000000001)
    '0.3'

    >>> D(0.30000000000000004)
    Decimal('0.3000000000000000444089209850062616169452667236328125')

    >>> repr(0.299999999999999988897769753748434595763683319091796875)
    '0.3'

    >>> repr(0.30000000000000004)
    '0.30000000000000004'

    >>> print 0.30000000000000004
    0.3
It also matters whether you're using Python 2 or 3. This is one of the things they changed in 3:

    >>> print(0.1 + 0.2)
    0.30000000000000004
you are using Decimal "wrong". If you want it to handle exact values, you need to pass a string with the decimal interpretation

    Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 02:27:37) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from decimal import Decimal
    >>> Decimal('0.1') + Decimal('0.2') - Decimal('0.3')
    Decimal('0.0')
    >>> Decimal(0.1) + Decimal(0.2) - Decimal(0.3)
    Decimal('2.775557561565156540423631668E-17')
    >>> Decimal(0.1) == Decimal('0.1')
    False
In this case, that was deliberate. I was using it to show the exact decimal value of the floating point approximations to decimal numbers, not to do decimal arithmetic.
What does it mean to "pass" this test? Just that the software is massaging its output to match human expectations rather than giving the most accurate (if not the prettiest) answer?
Just say (0.1+0.2)*10-3. That works everywhere no matter the amount of rounding.
You've just invented fixed point numbers.
I'm not sure I follow? The point here was to show that a language can't represent the decimal fractions exactly.
But they can. Many languages have decimal and rational types.
> Just adding 0.1 to 0.2 in the language of your choice can be misleading because printing the number might just cut off the digits at some point.

It's amazing that someone went to the trouble to register a custom domain for this and put up a nicely formatted page, yet doesn't explain the above: that it's not (just) a matter of representation, but printing precision.

That's interesting, I had no idea. There's probably a way more detailed Rosetta Code style project that could cover all these nuances, but for a single page demo, there's only so much detail I can get into before it would be more confusing than helpful.

If you'd like to flesh out the perl section a bit though, maybe include the differences between 5 and 6, I'd be happy to merge a pull request.

You've had a perl6 pull request outstanding for a while. The differences are far too big to list simply though. Ultimately Perl6 uses Rationals by default, not Floats.
> For what its worth, perl6 passes this test with flying colours.

This is because Perl 6 decimal literals are stored in a built-in rational type called Rat, which internally stores numbers as ratios between integers. So that 0.1, 0.2, 0.3 are internally stored as the tuples (1,10), (2,10), and (3,10), and the decimal value you see is just the string representation and not the actual internal value.

If you want your decimal literal to be floating-point (called Num in Perl 6), you want to add e0 to the end.

To wit:

perl6 -e 'say 0.1+0.2-0.3' reveals '0'

perl6 -e 'say 0.1e0+0.2e0-0.3e0' reveals '5.55111512312578e-17'

(comment deleted)
I would like to see a language use decimal by default. Under the hood, it could use floating point for certain types of calculations where less precision is required. If I want to know the answer to 0.2 + 0.1, chances are I want the answer to be 0.3. If I'm writing performance-sensitive code, maybe I can drop down a level and opt for float(0.2) + float(0.1). Are any languages doing this currently?
Perl 6 uses rationals by default. They have the advantages of being base-agnostic, able to accurately represent any recurring digit expansion accurately regardless of eventual base, and also faster (since, especially if you normalize (convert to lowest terms) lazily, most operations are just a few integer instructions with no branching, looping, or bit-twiddling involved).
True. Also notice that if the user wants a floating point, the literal format is the one with the e<exponent> suffix:

    0.1e0, 1e-1, 0.2e0, 2e-1 etc.
You want COBOL. It did decimal by default 50 years ago.
> I would like to see a language use decimal by default.

So, Scheme?

Scheme has rationals, but decimal literals aren't rational by default. You have to type #e in front of it, or express it as a fraction.

It really ought to be fixed at the implementation level (how much stuff would it really break if floatin point errors went away?), but failing that I'd love a macro that did it for me. I can't figure out how to write one robustly.

Wouldn't you get other issues with decimal numbers? Consider 1/3 for instance.

What you really want, I think, is rationals by default, as in Perl 6.

For C# i see a different behaviour as in his example. double test = 0.1 + 0.2; //will be 0.30000000000000004 if you check in the Debugger Debug.Print(test.ToString());//will print 0.3
Go is actually a lot more confusing about this than this than this page makes clear.

a := .1; b := .2

fmt.Println(a+b) returns 0.30000000000000004

fmt.Println(.1+.2) returns 0.3

Even worse, this persists into comparisons:

c := .3

a+b == c returns false

.1+.2 == c returns true

It's not a particularly big issue, mostly, but I'm sure it's confused someone before now, and will do so again.

Demo code: http://play.golang.org/p/FOv0JQRQJN

One should never compare for equality on floats or doubles, with the possible exception of '0' depending on the context.
Don't be ridiculous. It's perfectly fine if you know what you're doing. For example, when you have a limited selection of floats (e.g. hashtable keys, or only numbers between 0 and 100, accurate to 1 decimal) - in these cases, strings would work just as well, but floats are more efficient (and compare correctly).
You've just redefined your problem to using floats as the underlying representation. In that case you don't even need floats...

In most other cases the correct way is to check if the absolute value of the difference between one value and another is smaller than some arbitrary cut-off, a small number typically denoted with 'epsilon'.

I don't need floats, but they're useful. E.g. for comparison, as I wrote above.
Until your code gets ported to a platform where float == double and it turns out the algo feeding your clever efficient comparison delivers slightly different values that previously collapsed into the same bucket. Relying on the particulars of an underlying representation requires deep knowledge of how those particulars are implemented (and I would happily agree with you that such knowledge is a requirement but more often than not when you come across a float comparison with equality you're looking at a bug. That you're the exception to the rule is fine with me, you seem to know what you're doing so don't take it personal).

The real problem is: most people don't have deep knowledge of how those details are implemented and take 'equality' for granted, especially if the visual representation of the numbers they are comparing tells them they should be equal which is not always the case.

The old adage goes: if you need floating point you don't understand your problem :)

The comment that sparked this sub-thread is a nice illustration of what can and does go wrong.

try " fmt.Printf("%.50f\n", .1+.2)"
In Haskell/GHC it depends on what representation you chose. Default is Double, which exhibits this problem. But if I force it to Float I get the 'correct' result. Or if I use the 'Scientific' data type (which has arbitrary precision). Just like 'Int' vs 'Integer', where the former is restricted by the machine's word size, while the later is unbounded.

    Prelude> 0.1 + 0.2 :: Float
    0.3
    Prelude> 0.1 + 0.2 :: Double
    0.30000000000000004
    Prelude> :m +Data.Scientific
    Prelude Data.Scientific> 0.1 + 0.2 :: Scientific
    0.3
(comment deleted)
So effectively, in Haskell, it's both and neither.

A Float will round, as expected.

A double will return the correct value as it, should.

Shouldn't the Scientific, if it has arbitrary precision, arrive at the correct value too?

Clearly the only logical conclusion is that the Glasgow compiler, at its roots, virtualizes the entire quantum mechanics model as a compilation step, enabling superposition-as-a-feature.

My futile attempts at humour aside, this is quite an interesting thing to know and exactly the kind of intricate little detail I was hoping for.

Tip of the hat, friend.

The Scientific type does arrive at the correct value. 0.3 is the correct answer, and Scientific gives it to you. I don't know why you would expect something else from a type which implements arbitrary precision arithmetic.
Its important to understand that 0.1 + 0.2 is actually 0.3, and Scientific is giving the correct answer.

The Haskell type annotations aren't casting values that are interpreted as binary floating point (as might be the case in some languages), it is instructing the compiler how to treat the literals. Scientific is arbitrary precision decimal, so it can exactly represent 0.1 and 0.2, and return the exact result 0.3, rather than representing 0.1 and 0.2 as binary floating point approximations, adding them, and then producing some decimal approximation of the binary floating point result when asked.

So I have a question.

For those that arrive at the right answer, is it by mathematical correctness in the implementation or by rounding down?

i.e. Which are right and which are flukey in their wrongness?

Under any other circumstance I wouldn't care about this pointless detail, but seeing as how this is the top post on the front page, I'd like an excruciatingly detailed investigation please.

Don't make me not get round to doing it myself, sir.

In PHP's case, it converts the value to a printable (string) representation, and in so doing rounds off to a precision of 14 digits.

http://lxr.php.net/xref/PHP_5_4/Zend/zend_operators.c#2142

The "mathematically" correct answer is 0.30000000000000004 according to the IEEE-754 spec.

Scheme and Common Lisp arrive at the right answer to (+ 1/10 2/10) by correctness. They have internal representations for rational numbers.
Read http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/pr... and, if you still need more info, a few of the earlier papers it references.

All of them may be correct as typically, multiple 'answers' can be correct. One could argue that given a number, one should print the shortest string that round-trips to that number, but that may mean that one prints "0.3" while the 'real' value is closer to 0.30000000000000004 or 0.29999999999999998 (a representation that one of the algorithms in the above paper gives) or possibly even equal to it.

Also, some of them may be incorrect for backwards compatibility reasons. In the eyes of some, that makes them correct again.

And don't get round to not doing it yourself :-)

anyone understand why is it 0.30000000000000004, why not 0.30000000000000002 or 0.30000000000000003 or something else?
Floating point is (usually) about "closest representable value". That is, the normal floats form a finite subset of the real numbers, and a calculation like x + y will pick the closest element to the true result.

In this case, adding the closest floating point value to "0.1" (0x1999999999999a / 2^56) and the closest one to "0.2" (0x1999999999999a / 2^55) gives

  0x13333333333334 / 2^54
which is exactly

  0.3000000000000000444089209850062616169452667236328125
(Rational numbers with a denominator that's a power of two have a finite decimal representation, and that's the full one for this number.)

However, when printing, one is either interested in a close-enough rounded value (e.g. like some languages print 0.3 by default, or how %.3f will explicitly limit to 3 digits), or an exact representation. The exact representation should be round-trippable, so when you read it back in, you get exactly the same value. In practice this means printing a float z as a decimal value such that the closest float to the exact decimal value (i.e. as a real number) is z. Obviously the full string above works because it is exact, but it is nice to be as short as possible. In this case, the floats immediately before and after our number are exactly

  0.299999999999999988897769753748434595763683319091796875
  0.300000000000000099920072216264088638126850128173828125
So, only printing up to the first 4 is enough to uniquely identify our float: it is the closest one.

The 0.2999* float is slightly interesting: it is the closest float to 0.3, and most environments will print "0.3" in exact mode.

There's a lot of trickery/research around printing[1] and operating on floats: rounding things correctly can be highly non-trivial[2].

[1]: e.g. http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf

[2]: https://en.wikipedia.org/wiki/Rounding#Table-maker.27s_dilem...

Why don't all these languages fold down to the same FPU add and hence produce the same output (on the same hardware)?

Are you telling me they implement their own version of the IEEE float standard? Surely that way madness lies.

The difference is in how the result is converted to a string, not in how the result is produced.
The difference is between the length of their floating-point number (e.g. float vs double).
In some cases, string conversion is different, and the same calculation is going on, but the output is different.

In some cases, the internal representation isn't binary floating point at all, so the errors resulting from approximating an exact decimal with a binary float don't occur.

They're close enough for government work.
Solid proof that C# is better than JAVA. :D
The Common Lisp version is slightly misleading because computations are made by default with single-float types, whereas there is also short-float, double-float and long-float.
default for READ-DEFAULT-FLOAT-FORMAT is implementation dependent and can be changed by user.

In CL type float has subtypes single-float, double-float, short-float, and long-float. "Any two of them must be either disjoint types or the same type; if the same type, then any other types between them in the above ordering must also be the same type. For example, if the type single-float and the type long-float are the same type, then the type double-float must be the same type also."

If there is only one float representation it has to be `(typep x 'single-float)` but if the internal representation is different (double-float for example) it can be all of the types. At least this is how I understand the spec.

http://clhs.lisp.se/Body/v_rd_def.htm#STread-default-float-f...

http://clhs.lisp.se/Body/t_float.htm#float

http://clhs.lisp.se/Body/t_short_.htm

Indeed. Note also that there are recommended minimum precisions and exponent sizes, which are followed in practice by CL implementations. That means that if double and long are the same type, the precision and size is at least the one recommended for long-float.