Here's something that would probably help lessen the confusion: don't allow or atleast warn about implicit conversions in binary floats in source code literals. float foo = 0.3; // warning: invalid binary float value float bar = 0.3f; // ok, converts into nearest bin float decimal qux = 0.3m; // ok decimal feh = 0.3; // why not; will be exactly 0.3 All in all, languages go for decimal floats by default but then the i…
0.30000000000000004
131–140 of 140 posts
Re: 0.30000000000000004
#132How does Postgres "work" properly? I was wondering this the other day. Is something magical going on behind the scenes that I should know about? I added a ::decimal to be safe, but I wasn't sure
Re: 0.30000000000000004
#133Previous discussions: https://news.ycombinator.com/item?id=10558871 (1.5 years ago, 240 comments) https://news.ycombinator.com/item?id=1846926 (6.5 years ago, 128 comments)
Someone should figure out the mean length of time between article repeats on HN. And then find some correlation with university degrees and job tenure.
Re: 0.30000000000000004
#134Once I wrote a library for double-to-string conversion and vice versa, which handles such roundings nicely: https://github.com/mkupchik/dconvstr Key idea is not just to map binary floating point value X to a decimal floating point value Y, but instead (in extended precision, with 64-bit mantissa) compute an interval of decimal floating point values [Y1, Y2] which maps back to X (in standard precision, with 53-bit man…
Python does this, for example (but likely in a more efficient way): http://bugs.python.org/issue1580 See also http://www.netlib.org/fp/ http://web.archive.org/web/20060908072403/http://ftp.ccs.neu... So for instance 0.29999999999999993 + 0.00000000000000003 -> 0.3, but 0.30000000000000002 -> 0.30000000000000004 Note that this will still not solve the 0.1 + 0.2 problem from the OP, since the nearest float to 0.3 is no…
Or even more precisely, the nearest float to 0.3 is not actually the same as the nearest float to (the nearest float to 0.1) + (the nearest float to 0.2).
Re: 0.30000000000000004
#135SBCL: * (+ (/ 1 10)(/ 2 10)) 3/10
It's great that your language of choice has fraction support - but real-world engineering problems rarely involve simple rational fractions exclusively. At some point you're going to run into roundoff error. Plus, fractional computing has severe problems with expanding denominators, which grow very quickly with non-trivial operands. Although computers are fast, the overhead of dealing with hundred-digit-long arbitrar…
Re: 0.30000000000000004
#136It's actually pretty simple. When you have a base 10 system (like ours), it can only express fractions that use a prime factor of the base. In a way, not so simple (obvious to you? not to me)
It's actually pretty simple, if you've taken prime number theory. FTFY
Or if it's explained a bit more intuitively.
A terminating decimal fraction 0.xyz (in base 10) is basically
xyz / 1000
Now, 1000 = 2x5 x 2x5 x 2x5.So, if you have any fraction n / d, and the denominator d has only factors 2 and 5, then you can "fill up" the remaining 2's or 5's to get a power of 10, and write it as a terminating decimal fraction:
3 / 25 = 3 * 4 / (25 * 4) = 12/100 = 0.12
7 / 8 = 7 * 125 / (8 * 125) = 875/1000 = 0.875
etc.But, if the denominator d has any factors other than 2's and 5's, there is just no way you can multiply any further integers with it to get a power of ten.
A decimal floating point would then be not only 0.875, say, but 0.0000875 or 875000. So, now you say: 875/1000 x 10^E, where the 875 is your (3-digit) mantissa and the E your exponent. But that's still always an integer divided by a power of 10. So, if what we try to represent is a fraction with a denominator that has prime factors other than 2 and 5, there's no way we can represent it as a floating point decimal fraction.
Similarly, a IEEE 754 floating point is basically
10011010101 / 2^53 * 2^E
(with some abuse of notation, where the first part is a binary mantissa...). But, that's fundamentally still an integer divided by a power of 2. So, if what we try to represent is a fraction with a denominator that has prime factors other than 2, there's no way we can represent it as a floating point binary fraction, and therefore not as a Float64.Edit: use x for multiplication to avoid italics...
Re: 0.30000000000000004
#137 > (+ 0.1 0.2)
0.3
Why is that? Because I made a "populist" decision purely for the sake of better "optics". The default printing precision for floating-point values is set at just enough digits to truncate things in this manner: 2> *print-flo-precision*
15
The cost to doing that is that the printed representation of floats isn't guaranteed to have read/print consistency: what we print doesn't always read back as the same value which in other words means that we don't have a unique printed representation for each possible value. That default of 15 isn't my favorite language design decision. It's justifiable and I believe in it, but not as wholeheartedly as in some other design decisions.In any case, language users who need to record every bit of the IEEE 754 double in the printed representation are not just hung out to dry: they can set or override this special variable to 17.
3> (let ((*print-flo-precision* 17)) (prinl (+ 0.1 0.2)))
0.30000000000000004
0.3
(One value here is the `prinl` output, under the dynamic scope of the special variable override; the other is the result value, printed in the REPL, with the top-level binding of it, still at 15 digits.)Oh, and by the way, we dohn't have to add 0.1 to 0.2 to demonstrate this. Either of those values by itself will do:
4> (let ((*print-flo-precision* 17)) (prinl 0.1))
0.10000000000000001
0.1
5> (let ((*print-flo-precision* 17)) (prinl 0.2))
0.20000000000000001
0.2
Why 15 is used the default precision rather than, say, 14, is that 15 still assures consistency in one direction: if any number with up to 15 digits of precision is input into the system, upon printing it is reproduced exactly in all 15 digits. And 15 is the highest number of digits for which this is possible with an IEEE 64 bit double.Re: 0.30000000000000004
#138My personal view is decimal arithmetic should be the default and floating-point should be the library in scripting languages. And probably also Java/C#/Go-type languages.
Decimal only takes care of a subset of cases. I.e. those where the source data nicely lines up in decimal. Good for finance (although I think Britain made a mistake when it gave up the old penny and the shilling). But even there, you sometimes have to divide by numbers like 12 and 365. The point is NO representation is going to be idiot proof. So you just need to make a sufficient set of representations easy to use,…
Re: 0.30000000000000004
#139Earlier quoted context omitted.
Python does this, for example (but likely in a more efficient way): http://bugs.python.org/issue1580 See also http://www.netlib.org/fp/ http://web.archive.org/web/20060908072403/http://ftp.ccs.neu... So for instance 0.29999999999999993 + 0.00000000000000003 -> 0.3, but 0.30000000000000002 -> 0.30000000000000004 Note that this will still not solve the 0.1 + 0.2 problem from the OP, since the nearest float to 0.3 is no…
But Python also warns about it in the documentation, and provide the factions ( https://docs.python.org/3.1/library/fractions.html ) module and the decimal module t https://docs.python.org/3.1/library/decimal.html ) in the stdlib to avoid the problem.
Re: 0.30000000000000004
#140Earlier quoted context omitted.
Just a heads up - we're moving on from the unums standard. https://youtu.be/aP0Y1uAA-2Y
A note on the posit (aka sigmoid) standard that Gustafson discusses in the video: 1. Main feasibility benefit: behaves like IEEE 754 numbers in terms of accuracy. 2. Main performance benefit: able to match IEEE 754 precision & accuracy in fewer bits (~1/2) than IEEE 754. 3. When the standard is finalized, will specify behavior that in IEEE 754 allows for a degree of interpretation, meaning that behavior may not be re…
1. Main feasibility benefit: behaves like IEEE 754 numbers in terms of accuracy.
I think you mean "...in terms of allocation and storage"
2. I would like to disclaim that posits improve over IEEE 754 precision & accuracy over some value ranges. Over others, it's worse. The argument though is that you're more likely to be using those value ranges than the ones where you want. It's also not nearly 2x. For 64-bits, for example you'll realistically get about 6-7 extra bits of precision around 1. The standard DOES require features like "exact dot product" operation, which can allow you to do things that are "impossible" with the standard 754 spec; for example, exact solutions of systems of linear equations using convergent methods.
The way I've been thinking about it of late, is that floats and posits are a 'lossy compression of the real number line'; posits make better choices about how to compress this information, but there are of course tradeoffs.
The rest of it is great!