I remember in college when we learned about this and I had the thought, "Why don't we just store the numerator and denominator?", and threw together a little C++ class complete with (then novel, to me) operator-overloads, which implemented the concept. I felt very proud of myself. Then years later I learned that it's a thing people actually use: https://en.wikipedia.org/wiki/Rational_data_type
0.30000000000000004
281–290 of 422 posts
Re: 0.30000000000000004
#282Earlier quoted context omitted.
I think you mean that storing money in floating point is always terrible for accounting . Not all of finance is accounting. Imagine you work at a hedge fund, and you have a model that predicts the true value of some option. Assume the option is trading for $3.00. You do not really care if your model spits out $3.5 or $3.5000000001, you are going to buy either way. And your model probably involves a bunch of transcend…
I think the "floating point are bad for storing currencies" is one of the most common misconception about floating point. Most people don't realize that the IEEE-754 single precision floating point represent real numbers with 9 decimal digits (or 23 binary digits). The double, on the other hand, represents the real numbers with 17 decimal digits. This means that the double error UPPER BOUND is (0.00000000000000001)/2…
No, they don't. They merely can be converted back to decimal with those numbers of significant digits without loss of information.
That is important because (a) if this matters, you have to make sure you actually control the number of significant digits when converting to decimal, or you might end up with a different decimal, and (b) the operations that you do on the floats do not reliably behave as if there was the supposedly represented decimal number stored in them.
Now, sure, you can use floats for currency, if you know what you are doing, but the point of the warning against it is that you have to know what you are doing, and chances are you don't, or if you do, then you know where you can ignore it anyway.
(That is, unless you mean nothing more than that you can encode the information contained in an n-digit decimal in a float/double--which of course is true, but not particular to floating point numbers, as any state with a certain number of bits can, of course, encode any information of no more than that many bits, somehow.)
Re: 0.30000000000000004
#283Earlier quoted context omitted.
> Integer math (on typical machines) is an exact model of the ring of integers modulo 2^64. And even this is only true if you retrict yourself to unsigned integers. For signed integers you have quirks (-0x8000.. = 0x8000..) or minefields (undefined overflow semantics in C, which can yield non-associativity, tests deleted by the compiler, etc.). And I'd argue that whoever understands the ring of integers modulo 2^64,…
> And even this is only true if you retrict yourself to unsigned integers Fair point. I've edited my comment to include the word "unsigned". > I'd argue that whoever understands the ring of integers modulo 2^64, will also understand the IEEE754 semantics I'm an existence proof that that is not true :). Although I'm sure I could learn the IEEE754 semantics if I put enough effort into reading the spec. But even if they…
> I'm an existence proof that that is not true :). Although I'm sure I could learn the IEEE754 semantics if I put enough effort into reading the spec.
This was sloppy writing on my side. I wanted to say "whoever understands the ring of integers modulo 2^64, can also understand". And I'm sure you could :)
And you don't even have to read the spec. The core idea (mantissa, exponent, and sign) is super easy and writing a FP emulation for addition and multiplation is a really nice task to understand what is actually going on. The only really unfamiliar idea is binary fractions and I think this is a cool idea to understand on its own.
> But even if they don't know the word "ring", I think most programmers do understand how modulo arithmetic works, and they have algebraic intuitions about it that turn out to be true: both operations are commutative and associative, multiplication distributes over addition, equality is true if it's true in the actual integers, and so on.
Well that is all fine but scrolling back to the grand grand grand parent: That would also be a completely wrong abstraction to model financial stuff. I'm not saying FP is the solution, but for sure modulo arithmetic is also how you not want to do finance :)
Re: 0.30000000000000004
#284Earlier quoted context omitted.
And that roughly captures the spot where I was seeing doubles used. Yes, they could have used fixed point. I am guessing that what happened is that someone who had thought way more deeply about this than I ever needed to (I worked on the accounting side, where, yep, we always used decimals) either determined that, where the modeling was concerned, floating point errors were not worth worrying about, or estimated that…
To see 0.1 error using _double_ you have to do at least 2*10^17 operations (assuming the worst case scenario and no subnormals). If you are working with such huge numbers, 0.1 cents is probably a cost you are willing to pay to avoid expending thousands in a software solution. The saving with power saving using a floating point is likely greater than power your computers will have to expend to get a precise solution.
fn main() {
let x: f64 = 9007199254740992.0;
assert_eq!(x + 1.0, x);
}Re: 0.30000000000000004
#285Earlier quoted context omitted.
FWIW, both of those can be expressed exactly by floating-point numbers ;)
Right all integers up to 2^53 (or something like that) can be (in double precision). I assume that's the reason they made the mantissa linear, even though having the whole thing logarithmic makes more sense.
This is how positional number systems work at all.
Re: 0.30000000000000004
#286Earlier quoted context omitted.
I think you mean that storing money in floating point is always terrible for accounting . Not all of finance is accounting. Imagine you work at a hedge fund, and you have a model that predicts the true value of some option. Assume the option is trading for $3.00. You do not really care if your model spits out $3.5 or $3.5000000001, you are going to buy either way. And your model probably involves a bunch of transcend…
I agree with your overall point: it most likely does not matter when the values are close enough. However :) There can be two companies with 100M market cap. Corp A has issued 10M shares @ 10 each, Corp B has 10B shares priced at 0.01 A +/-0.001 change in Corp A share price is just 0.01% and moves the market cap by +/-10k, so probably nothing significant. The same nominal change in Corp B amounts to 10%, or +/- 10M i…
Re: 0.30000000000000004
#287I remember in college when we learned about this and I had the thought, "Why don't we just store the numerator and denominator?", and threw together a little C++ class complete with (then novel, to me) operator-overloads, which implemented the concept. I felt very proud of myself. Then years later I learned that it's a thing people actually use: https://en.wikipedia.org/wiki/Rational_data_type
Re: 0.30000000000000004
#288Earlier quoted context omitted.
To see 0.1 error using _double_ you have to do at least 2*10^17 operations (assuming the worst case scenario and no subnormals). If you are working with such huge numbers, 0.1 cents is probably a cost you are willing to pay to avoid expending thousands in a software solution. The saving with power saving using a floating point is likely greater than power your computers will have to expend to get a precise solution.
You can get a larger error than that using one operation. fn main() { let x: f64 = 9007199254740992.0; assert_eq!(x + 1.0, x); }
When adding numbers with large magnitudes differences (around 10^17 I think) it might exceed the format precision. I should have taken that in account when defining the error boundaries.
In dollars, you start having issues with cents when working with a tens of trillions.
For the vast majority of people this won't be an issue.
Re: 0.30000000000000004
#289Earlier quoted context omitted.
But rationals are more expensive to compute with (compared to floating-point; this is another example of the trade-off between performance and accuracy.)
it's not just that they are expensive, it's that there is a nondetermistic compute time. Let's say we need to do a comparison. Set a = 34241432415/344425151233 and b = 45034983295/453218433828 Which is greater? Or even more feindish, Set a = 14488683657616/14488641242046 and b = 10733594563328/10733563140768 which is greater? By what algorithm would you do the computation, and could you guarantee me the same compute…
Re: 0.30000000000000004
#290Earlier quoted context omitted.
Currency handling is almost never done with rationals (numerator and denominator) and is frequently (and correctly so!) done with fixed or floating point decimal types.
I develop accounting software for banks, brokerage houses and likes. Currency, taxes, rebates, etc. handling is NEVER done with floating point. Whatever you do with money you need predictable, reproducible results. It is norm that calculations are checked by software at two companies on both sides of transaction. Any discrepancies are alarms, bug reports, unhappy customers. Every significant operation is exactly spec…
Nonsense. I’ve seen real banking code at reputable banks that uses floats.
> Whatever you do with money you need predictable, reproducible results.
Floats aren’t random. They’re perfectly deterministic, predictable and reproducible. If you do the same operation in two places you get the same result.