The implementations of the languages are something else, however. I suggest the fine article by Bruce Dawson:
https://randomascii.wordpress.com/2013/07/16/floating-point-...
If you don’t know who Paul Zimmermann is, you may safely assume that he knows more about floating-point than you do (actually that he knows more about computer arithmetic than you do). While there may well be errors or typos in the slides, it’s far more likely that you are misinterpreting something without the context of the talk that went along with them.
Most programmers realize they are 'approximate' but they don't notice consequences, such as checking for exactly equal to zero, when you need to check for "close enough" depending on your context.
More subtly, but perhaps more damaging, they break properties like associative and commutative. Different orders change values, you can add tiny values repeatedly to a large value, and it won't change, breaking testing of the value...
The presentation goes over many of these, but the fundamental problem, to me, is that we're falsely presenting an interface suggesting "this acts like a number" when it doesn't in many key ways. We should perhaps call them approximate pseudo numbers or something. Don't allow operators, require functions like SortOfMaybeAdd, AcculumateList...
Perhaps such measures will communicate better. A fascinating world we program in...
Most of what C-family languages do has the wrong semantics. Any program which is happy to accept round-to-even and arbitrary increases in precision would probably accept the rearrangement of arithmetic under the assumption that it was commutative, but in e.g. Java there's no way to change rounding mode, and also no way to just request an approximate answer. The defaults are a compromise that satisfies no-one.
The only thing that they make easier is parsing and displaying numbers. The number of times where you deal with things that are guaranteed to be perfectly represented by powers of ten are relatively few.
For a physicist, a few. For an accountant, all.
Scheme (via its numeric tower) and some other language default to exact types (decimal or in some cases rational if they don't have a separate exact decimal type) and only use approximate floating point types if explicitly requested (including using by using a function which inherently produces an approximate output.)
Haskell can be configured by way of the default specifier to do something analogous to this in its handling of ambiguous numeric types (which includes literals, which start out with a generic type of Num a => a) , though the default default uses double precision floating point for types that can't be constrained to arbitrary precision integers -- default (Integer, Double) -- so to do this you need something like default (Integer, Rational, Double.)
No: commutativity of multiplication and addition still holds, but associativity indeed does not. That associativity does not hold is a much more unpleasant than if associativity would hold but commutativity wouldn't (the mathematics of common associative, but non-commutative structures is understood quite well, say (non-abelian) groups and monoids).
#include <stdio.h>
int main() {
unsigned long long bits = 0x7FFC000000000000;
double x = *(double *)&bits;
bits += 1;
double y = *(double *)&bits;
double z = x + y, w = y + x;
printf("%llx %llx\n", *(unsigned long long *)&z, *(unsigned long long *)&w);
}
Of course, this probably doesn't matter for most applications.The one that surprised me was that, in some cases:
Large + small + small + small =! small + small + small + = Large
Which is indeed associativity.Really underscores how complex things are.
a ( b + c ) != a * b + a * c
(in some cases)More importantly, we just shouldn't make them the default types used for decimal literals (or, a fortiori, for all numbers in your language -- yes, JavaScript, I'm looking at you.)
Premature optimization is the root of all kinds of evil -- or at least errors -- in programming, and "treat decimal literals as floating point because that's the most processor-friendly way of dealing with non-integer numbers" is the most pervasive premature optimization in computing.
Well... maybe. I don't think most people have the foggiest idea what floating point numbers are, or what the Real numbers they approximate are, so the idea that people (a) have a solid understanding of Math (let's just say Real Analysis) and that (b) this solid understanding is violated by the behavior of floating point numbers is a bit of a stretch.
E.g. I doubt many programmers realize that 1/10 doesn't have a finite binary expansion.
(And if you can actually read the slides by themselves the presentation is probably not very good).
EDIT: I don't normally care much about a "downvote", but when it must be backed by ignorance I have to comment. Excel has always, and to this day, uses doubles. There is not a whit of decimal math to be found in the product. I kind of know this given that my industry is finance, and the calculation quirks of Excel (which is simply a quirk of floating point math) are something that the industry is aware of and works around.
Now it by default rounds the number for presentation, but surely no one on HN would be so confused that they fail to understand the difference.
1. a and b have the same default displayed representation
2. a and b compare equal
3. (a - b) is zero
It’s been ~10 years since I really poked around at Excel numerics, so my recollection may not be perfect, but there were definitely a number of hacks beyond straightforward double-precision arithmetic when I looked at it.On topic: do you happen to know why Excel doesn't use decimal math? I'd imagine for most spreadsheets the performance difference shouldn't be noticeable.
On the first look, it appears a dubious presentation anyway. The examples don't have much sense too: you can't actually calculate properly sin( 10e22 ) in binary floating point format because both exponent and mantissa are binary: the exponent is 2^76 and in 4-bytes floats you have 23 bits, in 8-byte doubles 53 bits, so you can't even represent the starting number with the enough bits to calculate meaningful sin. You'd need a two parameter function to be even able to pass what you actually want to pass. So calling sin( 10e22 ) in the languages with 4 or 8-bytes binary FP of course can't be expected to produce any meaningful result.
But maybe all this was spoken, not written, and it's just misleading to read it without knowing the comments.
"Who is NOT using: Excel, BLAS" could maybe then mean that author wanted to say "Excel, BLAS, they use them too"? (If we want to believe that he had any idea what he wrote about). Definitely not good to give this presentation to somebody who doesn't already know more. Confusion guaranteed.
Also 1e22 (note: not 10e22) is exactly representable in double (though not in float). Specifically, it is exactly equal to:
1.000011110000110011110000011001001101110101011001001 * 2^73
which requires only a 52 bit significand.So the author is: http://en.wikipedia.org/wiki/Paul_Zimmermann
Seems to mean floating point according to this...: http://www-classes.usc.edu/engr/ce/108/text/fbk01.htm
Please correct me if I'm wrong, I'm not that familiar with Fortran.
Floats are not that much of a problem as people might think. They come as a surprise for those that don't know how they work. Small course in numerical analysis would do people good.
Switching to fixed point would not cure anything, just change the ways they can break. Floats actually have way superior error propagation properties compared to fixed point if your range varies widely, as it often does in physics. In a massively simplified way one can say that multiplication is error free in floats whereas in fixed point multiplication is horrible.
Dealing with the bigger problems (like combining arithmetic with transcendental functions and then getting bitten by approximation issues) require using a symbolic math engine and only reducing to numeric approximations when needed (e.g., for presentation) rather than for intermediate values.
"Throw floats at the problem and hope that it works okay" is a great approach for performance, but not really the best approach for correctness. Which is fine, if its a conscious decision, but its so pervasive and encouraged by most programming environments that its easy for people to forget the tradeoff that they are making.
Although the naming schemes for BLAS routines do explicitly reference "single precision" and "double precision" so now I'm confused too.
IEEE-754 64-bit decimal is also a floating point representation:
Sure, but if you aren't dealing with transcendental functions but just typical input from people and arithmetic, you don't usually introduce as many errors by rounding to a base-10 representation, so decimal floating point solves a lot of the problems (at the cost of performance if you have a system that implements it over hardware that doesn't.)
You still have the big + small problem, though.
But when you want to represent something like money, there's by definition an error as soon as you want to store 0.01 as the binary FP, and the same error doesn't happen in decimal FP.