-
Notifications
You must be signed in to change notification settings - Fork 233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Potential loss of precision due to floating-point numbers #23
Comments
I started to create very simple, almost trivial tests, but stopped quickly, as I noticed this issue became very relevant. The tests follow the scheme:
Trade details and RPC output available in "standard output": Some more via Boost tests with plain typedef boost::multiprecision::cpp_dec_float_100 XDOUBLE;
check XDOUBLE(1) / XDOUBLE(2) == XDOUBLE(3) / XDOUBLE(6) failed [0.5 != 0.5]
check XDOUBLE(100) / XDOUBLE(200) == XDOUBLE(120) / XDOUBLE(240) failed [0.5 != 0.5]
check XDOUBLE(6) / XDOUBLE(2) == XDOUBLE(9) / XDOUBLE(3) failed [3 != 3]
check XDOUBLE(6) / XDOUBLE(9) == XDOUBLE(2) / XDOUBLE(3) failed [0.666667 != 0.666667] For the comparison operators using check std::string("2.0") < std::string("10.0") failed [result = false] I'm probably going to give Similar behavior was actually observed some time ago: mastercoin-MSC#198 |
This is really good data - I'm surprised at the
There was some discussion about the fit of float here (given all the rest of our math is integer based) but I honestly thought you had to get really precise before float math became an issue - to see 6/2 != 9/3 was quite an eye opener. |
I think the underlying issue is that the float point representation of 6/2 and 9/3 differs slightly, and this underlines why I'd prefer to use no float at all. As it looks like, the initial plan was to convert the results into strings, but this comes at a huge price: it must be done right, e.g.:
... and it introduces a significant performance hit. As mentioned, I'll give |
Just dumping some comments from the Skype chat in here: Patrick FYI the bitgo stuff they're talking about got sorted because the mining pool was kind enough to return the 85 BTC processed as tx fee, but I think the moral of the story they're trying to get across is "floats are bad m'kay". As I mentioned I'm not particularly knowledgable in this area, but if a particular float calculation cannot be guaranteed to return the same result given the same input values in all systems, I also agree we can't use them. |
Looks like I found a sequence:
These offers should match, but currently don't, and the comparison fails here: // Is the desired price check satisfied? The buyer's inverse price must be larger than that of the seller.
if (pnew->inversePrice() < sellers_price) {
file_log("SKIP: %s < %s\n", xToString(pnew->inversePrice()), xToString(sellers_price));
continue;
}
|
Isn't that because |
Hehe, this is what I assumed too, but in this case it's:
The issue above is indeed based on the floating numbers, and probably like |
OK cool - FYI I found that if I upped |
It is given that certain rational numbers can not be represented as floating point with any finite precision, and it's representation would have an endlessly repeating sequence, resulting in an approximation of the actual value. While this likely only affects the fractional part of numbers, there are other pitfalls, and depending on the level of precision, there are gaps between numbers (for example
9007199254740993
doesn't exist, when usingdouble
).At this point it is unclear, if the meta DEx might be affected by similar issues, which currently uses boost::multiprecision::cpp_dec_float_100 to represent numbers, which are further transformed into strings and cut after a certain number of digits. This primarily affects the calculation of unit prices, and related values, such as the updated amount desired by a seller, the updated amount desired by a buyer, the number of units a buyer receives, and the order matching, which is based on the unit price.
To avoid potential pitfalls related to floating-point numbers, suggestions so far were to use boost::rational to represent rational numbers, or to use boost::multiprecision::int128_t with plain integer math for the core calculations.
It should be discussed, whether this is an actual issue, and how it should be addressed.
The text was updated successfully, but these errors were encountered: