News for hackers. Stuff that matters.
Re:0.30000000000000004 by Tepix on Friday November 13, 2015 @05:55AM (#10559123)
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.

Re:0.30000000000000004 by philh on Friday November 13, 2015 @07:23AM (#10559414)
> 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.

Re:0.30000000000000004 by philh on Friday November 13, 2015 @07:54AM (#10559528)
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
Re:0.30000000000000004 by _ZeD_ on Friday November 13, 2015 @08:10AM (#10559608)
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
Re:0.30000000000000004 by philh on Friday November 13, 2015 @08:17AM (#10559648)
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.
Re:0.30000000000000004 by evanpw on Friday November 13, 2015 @08:05AM (#10559581)
There's some discussion here: https://docs.python.org/3.1/tutorial/floatingpoint.html. Basically: eval(repr(x)) == x is guaranteed, and some attempt is made to give the shortest such string representation for Python 3.1+ (and 2.7?), but it's not guaranteed on all platforms.
Re:0.30000000000000004 by indosauros on Friday November 13, 2015 @08:06AM (#10559588)
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
Re:0.30000000000000004 by jamesdutc on Friday November 13, 2015 @08:03AM (#10559575)
I believe this is relevant: https://bugs.python.org/issue1580
Re:0.30000000000000004 by static_noise on Friday November 13, 2015 @06:35AM (#10559254)
> 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?

Re:0.30000000000000004 by panic on Friday November 13, 2015 @07:45AM (#10559504)
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).

Re:0.30000000000000004 by static_noise on Friday November 13, 2015 @08:48AM (#10559786)
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?

Re:0.30000000000000004 by gsnedders on Friday November 13, 2015 @09:18AM (#10559942)
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".

Re:0.30000000000000004 by tinco on Friday November 13, 2015 @09:01AM (#10559852)
I think so, yes.
Re:0.30000000000000004 by amyjess on Friday November 13, 2015 @11:34AM (#10560692)
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.
Re:0.30000000000000004 by Piskvorrr on Friday November 13, 2015 @06:50AM (#10559306)
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!").
Re:0.30000000000000004 by kazinator on Friday November 13, 2015 @09:18AM (#10559943)
> 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.

Re:0.30000000000000004 by taneq on Friday November 13, 2015 @07:33AM (#10559453)
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?
Re:0.30000000000000004 by amyjess on Friday November 13, 2015 @11:31AM (#10560676)
> 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'

Re:0.30000000000000004 by erikwiffin on Friday November 13, 2015 @10:07AM (#10560180)
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.

Re:0.30000000000000004 by hahainternet on Friday November 13, 2015 @10:23AM (#10560286)
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.
Re:0.30000000000000004 by marcosdumay on Friday November 13, 2015 @06:51AM (#10559308)
Those 0.1, 0.2, and 0.3 numbers do not have any floating point representation at all. Computers just can not use them.
Re:0.30000000000000004 by skrebbel on Friday November 13, 2015 @07:25AM (#10559419)
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.

Re:0.30000000000000004 by lsaferite on Friday November 13, 2015 @07:51AM (#10559517)
I thought finance worked using integers internally for this exact reason.
Re:0.30000000000000004 by dragonwriter on Friday November 13, 2015 @05:26PM (#10562799)
"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.

Re:0.30000000000000004 by ars on Friday November 13, 2015 @12:47PM (#10561157)
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.

Re:0.30000000000000004 by lsaferite on Friday November 13, 2015 @04:44PM (#10562595)
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.

Re:0.30000000000000004 by mehrdada on Friday November 13, 2015 @11:58PM (#10564530)
> Do CPUs support doing math directly with BCD values

Perhaps surprisingly to some, x86 has instructions for BCD manipulation at the CPU level. Legacy.

Re:0.30000000000000004 by jerven on Saturday November 14, 2015 @04:06PM (#10567257)
In IBM POWER 6 this returned as an optimisation in hardware.

https://books.google.ch/books?id=SPXHAgAAQBAJ&pg=PA44&lpg=PA...

Re:0.30000000000000004 by mannykannot on Friday November 13, 2015 @08:33AM (#10559723)
Maybe for accounts bookkeeping, but I doubt it is feasible, or even desirable, for financial statistical work.
Re:0.30000000000000004 by roblabla on Friday November 13, 2015 @08:00AM (#10559559)
I figured they used Binary Coded Decimal [0] when they need to support decimals.

[0] : https://en.wikipedia.org/wiki/Binary-coded_decimal

Re:0.30000000000000004 by lsaferite on Friday November 13, 2015 @09:29AM (#10559983)
Well, I know from doing payment gateway integration that many of them use integers for currency amounts. They are the smallest currency unit and for display you then convert based on the currency specific precision value.
Re:0.30000000000000004 by astine on Friday November 13, 2015 @09:38AM (#10560027)
Accounting systems I've worked with do most of their logic in a RDBM and store monetary types using a 'decimal' type.
Re:0.30000000000000004 by Grue3 on Friday November 13, 2015 @07:03AM (#10559353)
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.
Re:0.30000000000000004 by amalcon on Friday November 13, 2015 @07:08AM (#10559364)
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.
Re:0.30000000000000004 by Kristine1975 on Friday November 13, 2015 @08:12AM (#10559621)
Since 2008 IEEE-754 also supports decimal floating-point: https://en.wikipedia.org/wiki/IEEE_754#Basic_and_interchange...

>There are three binary floating-point basic formats (encoded with 32, 64 or 128 bits) and two decimal floating-point basic formats (encoded with 64 or 128 bits)

Re:0.30000000000000004 by poizan42 on Friday November 13, 2015 @07:45AM (#10559507)
Just say (0.1+0.2)*10-3. That works everywhere no matter the amount of rounding.
Re:0.30000000000000004 by static_noise on Friday November 13, 2015 @08:49AM (#10559795)
You've just invented fixed point numbers.
Re:0.30000000000000004 by poizan42 on Friday November 13, 2015 @11:54AM (#10560816)
I'm not sure I follow? The point here was to show that a language can't represent the decimal fractions exactly.
Re:0.30000000000000004 by recursive on Friday November 13, 2015 @12:06PM (#10560893)
But they can. Many languages have decimal and rational types.