Posted by vismit2000 3 days ago
That said, some of these aren't alternatives. Symbolic computation is a different thing entirely. Interval arithmetic can be built atop floats (e.g. IEEE-1788) and has its own zoo of unintuitive behaviors. BCD is better called a historical artifact than an alternative these days.
It's really just rationals and decimal floats in this list, which probably don't solve the issues you have if you're considering float alternatives.
It's currently in use all over the world. You can't do a card payment, either in-person or online, without an intermediary using ISO8583.
1. Do you want your result to exactly encode the answer without rounding error? No fixed precision type can provide this in general, so you're stuck with symbolic approaches. If you can bound things (usually difficult), maybe you can get away with non-symbolic approaches.
2. Do you want a sensible numeric answer? This is what floats (and many other systems) give you. The definition of "sensible" is inherently tricky here and there's not a definition universally appropriate to every possible computation.
So let's return to 0.1+0.2=0.3000...1 specifically. There's two common ways to think of an encoded float. One is as the directly encoded value, as you're doing. Another way is to think of it as an interval of real numbers between the next lowest and highest intervals. Under this latter interpretation, it makes sense to discuss shortest decimal string within the interval, 0.3 in this case. There's no ambiguity because each real lives in exactly one interval. This is what algorithms like dragon box do for float to decimal string conversion.
What decimal floats give you is an encoding that tracks significant digits, where every decimal string exactly corresponds to a midpoint of an interval of reals. They do this at the cost of space, speed, and complexity. You don't get an escape from the fundamental issues of fixed precision types like rounding error, numerical sensitivity, precision loss, etc. I don't think that tradeoff makes sense for most algorithms in most contexts.
The benefit of sticking with floats is that lots of smart people have spent countless hours trying to give non-experts a "good enough" path through the untamed wilds of numerical analysis, tooling to help them when they get lost, tribal knowledge to point out the edge cases, and it's almost universally supported in hardware. By all means you should go wandering off the trail, but fully understand what you're doing and why beforehand.
Note that the result of 0.1+0.2 does not lie in the interval containing 0.3, which is was confuses most people. The issue is that there is some imprecision in representing 0.1 and 0.2 too, and that compounds when summing, resulting in something that does not actually correspond to 0.3 (hence the classic 0.1+0.2!=0.3)
That makes me think that I can just plan ahead by storing the number of cents instead of dollars, or a “hack”, and then it makes me wonder why the format even requires me to do that.
> 0.2+0.1
0.2+0.1 with floating point numbers _is_ deterministic, as you'll always get the same answer.
I suspect you might instead mean exact calculations/answers (in the example above, neither 0.1, 0.2 nor 0.3 have exact representations using floating point numbers).
And just to be clear, there are non-determinism-like issues with floating point numbers, but those are much rarer/niche and _can_ be fixed. For example parallel summation depends on the order the summation was made, so non-determinism in the parallel implementation ripples through the summation result. Some non-basic operations (e.g. trigonometric operations) have platform dependent implementations with different roundings, so you might experience different result based on the platform you're on.
> You just have to use other data types to handle money or complex mathematical operations like 0.2+0.1
Such as... decimal floating point.
Order of operations can change a result, for example. I suspect you mean that the algorithm never changes. While op means that mathematical operations which most folks would expect to be reliable are not.
Most folks won't encounter most of the issues, generally. But expose your code to a large enough dataset, or be like me and write a CAD/CAM system with motion control and experience most of them.
That's why I wrote hyperreal[2]
1: https://www.cs.tufts.edu/cs/40/docs/WhatEveryComputerScienti...
Sorry, yes, I probably should have worded that in a way that made the distinction more obvious.
You make it really hard to take you seriously.
When TZubiri made their original comment, I understood they were speaking about some or all of the issues outlined in the paper I linked. If you didn't, that's ok. If you think the referenced paper missed something, it's OK to add that.
> You make it really hard to take you seriously.
Same, bud.
When I said that floating points are not deterministic, I wasn't very precise, but I do think that broadly speaking, floating point arithmetic, as used today, foregoes determinism, and this results from the very ethos of the foundational IEEE754 data type, the goal is to have a data type for approximate answers, turns out that when exact answers are sacrificed in the name of speed, so is determinism. And this has huge effects on modern day, Floating Point is used on separate hardware with parallel operations, and there's race conditions that make most Machine Learning and AI computing irreproducible, and that indeed seems to be a consequence, as you mention, of the lack of associativity of FP.
So, that said, I would make two clarifications:
>-- Floating Points
>++ Floating Point computing
where by Floating Point computing would mean the actual application computing that we build, as opposed to "Floating points" referring to the ideal ancient standardized hardware layer abstractions.
And if necessary:
> -- is
> ++ tends to be
In order to be perfectly correct, which after all, is what we are going after.
So if pressed, I wouldn't say "Floating points are not deterministic" but "Floating Point computing tends to be non-deterministic", but I would feel very comfortable shorthanding it to "Floating Points are non-deterministic" anyways.
The paper cited is a bit hard for me, so I can't verify if it matches what I'm saying. But I imagine by the date, it wouldn't be able to address the issues that we can empirically from the advent of ML systems, but maybe it did foresee from a theoretical standpoint some of their limitations.
There's a between-the-lines thesis here that there's two main schools of computing nowadays, one that seeks perfection, and another that seeks approximations, the CPU/GPU dichotomy is roughly analogous to the Mathematics/Physics vs Engineering/Industrial dichotomy.rroot@t14:/mnt/c/Users/TomZubiri/Desktop# cat fixed.txt Thanks for following the thread. I'll clarify on my intended meaning was indeed a strict actual definition of determinism, but a broader definition of floating point, to include its actual usage. But fwiw, it was indeed possible that I was someone who confuses determinism for precision, but no.
When I said that floating points are not deterministic, I wasn't very precise, but I do think that broadly speaking, floating point arithmetic, as used today, foregoes determinism, and this results from the very ethos of the foundational IEEE754 data type, the goal is to have a data type for approximate answers, turns out that when exact answers are sacrificed in the name of speed, so is determinism. And this has huge effects on modern day, Floating Point is used on separate hardware with parallel operations, and there's race conditions that make most Machine Learning and AI computing irreproducible, and that indeed seems to be a consequence, as you mention, of the lack of associativity of FP.
So, that said, I would make two clarifications:
>-- Floating Points
>++ Floating Point computing
where by Floating Point computing would mean the actual application computing that we build, as opposed to "Floating points" referring to the ideal ancient standardized hardware layer abstractions.
And if necessary:
> -- is
> ++ tends to be
In order to be perfectly correct, which after all, is what we are going after.
So if pressed, I wouldn't say "Floating points are not deterministic" but "Floating Point computing tends to be non-deterministic", but I would feel very comfortable shorthanding it to "Floating Points are non-deterministic" anyways.
The paper cited is a bit hard for me, so I can't verify if it matches what I'm saying. But I imagine by the date, it wouldn't be able to address the issues that we can empirically from the advent of ML systems, but maybe it did foresee from a theoretical standpoint some of their limitations.
There's a between-the-lines thesis here that there's two main schools of computing nowadays, one that seeks perfection, and another that seeks approximations, the CPU/GPU dichotomy is roughly analogous to the Mathematics/Physics vs Engineering/Industrial dichotomy.
floating point arithmetic, as used today, foregoes determinism, and this results from the very ethos of the foundational IEEE754 data type
This was somewhat true in the past, but the situation has been improving dramatically in recent years to the point where FP determinism is completely feasible. The remaining hurdles are primarily on the toolchains/kernel side. I have a library called rfloat that you can drop into most C/C++ code for practical determinism without thought (subject to documented caveats), for example. You can do the same thing manually with some more careful attention.I'm in a very remote corner of the world on bad Wi-Fi though, so you'll have to forgive omitted links.
I have found in https://github.com/timschmidt/hyperreal and the geometry layers built atop it, that exactness and speed are not fundamentally opposed. Hyperreal retains exact mathematical structure at the scalar level, and at each level higher up through vertices, vectors, matrices, triangles, curves, etc. And uses it to choose optimal datatypes, dispatch against the fastest algorithms, and eliminate large chunks of needless calculation where simpler proofs are sufficient.
The downside is a bit of additional code and API complexity. But the slightly more complex API accurately represents issues like uncertainty in decidable equality of arbitrary computables in bounded time, which is a limitation of math itself, so representing it accurately in the API permits the algorithms to be more robust.
> Floating Point is used on separate hardware with parallel operations, and there's race conditions that make most Machine Learning and AI computing irreproducible
Deterministic reproducibility is, I think, one of the most awesome superpowers of a system like hyperreal. It makes simulation much more interesting and useful. As well as cross-platform use.
We use floats as a trade-off between speed and accuracy. IEEE 754 is a very reasonable trade-off for a wide range of applications, but if you can figure out where you need to trade speed to get more accuracy with e.g. one of the methods mentioned here, Herbie's gotcha covered.
I remember seeing some research about switching between formats, but I don't have anything to cite right now.
Multiplications are a little bit tricky. Multiplication by an integer is trivial. Multiplication of two fixed point numbers produces the result with the number of fractional binary digits equal to sum of the number of fractional digits in source numbers. The result may be stored in an extended type, truncated down or rounded.
Divisions work fine too, but sometimes may be slower compared to float types, because CPUs can for some reason do much faster floating-point divisions compared to integer divisions.
The only disadvantage of fixed-point numbers is that it's required to keep a balance between range and precision carefully. One can't just use some specific precision in the entire codebase, typically precision should be selected for each individual operation.
This hack has some interesting advantages. Float to integer is still only a few cycles, the masking is one line of libm functions, you get better (and dynamically selectable!) precision, it has gradual underflow and overflow, you can write numeric code like usual, and normalization is automatic.
The mantissa of a floating point number has less bits than the integer type of the same byte size.
It's an infinite precision exact constructive real with excellent performance characteristics and approximation only at explicitly named lossy export functions.
Some recent benchmarks: https://github.com/timschmidt/hyperlattice/blob/805d092d1d96...
Seen it used in a couple places. Logarithmic depth buffer is one. Yamaha DX7 is another.
https://frinklang.org/fsp/frink.fsp?fromVal=new+interval%5B-...