#12327 closed Bugs (fixed)
cpp_rational::convert_to<double>() does not return the nearest number
Reported by: | Owned by: | John Maddock | |
---|---|---|---|
Milestone: | Boost 1.62.0 | Component: | multiprecision |
Version: | Boost 1.61.0 | Severity: | Problem |
Keywords: | Cc: | komakisen@… |
Description
Hello,
I tested with the following program to see if boost::multiprecision::cpp_rational::convert_to<double>() returns the nearest double number to the exact value. I expected it to print only "true"s, but it actually prints some "false"s too. Is it a bug or (un)documented behavior?
#include <boost/multiprecision/cpp_int.hpp> typedef boost::multiprecision::cpp_rational NT; void test_convert_to_double(double d1, double d2) { NT r1(d1); NT r2(d2); NT two(2); NT three(3); NT r112 = (two * r1 + r2) / three; NT r12 = (r1 + r2) / two; NT r122 = (r1 + two * r2) / three; double rd1 = r1.convert_to<double>(); double rd112 = r112.convert_to<double>(); double rd12 = r12.convert_to<double>(); double rd122 = r122.convert_to<double>(); double rd2 = r2.convert_to<double>(); std::cout << std::boolalpha; std::cout << (rd1 == d1) << std::endl; std::cout << (rd112 == d1) << std::endl; std::cout << (rd12 == d2) << std::endl; std::cout << (rd122 == d2) << std::endl; std::cout << (rd2 == d2) << std::endl; } int main() { volatile double a = 0.099999999999999992; volatile double b = 0.10000000000000001; volatile double c = 0.10000000000000002; // prints true, false, true, true, true test_convert_to_double(a, b); // prints true, true, false, false, true test_convert_to_double(b, c); return 0; }
I compiled this program with MSVC 14.0 and ICL 16.0.3, both for x64 Debug build. They give the same results.
Change History (4)
comment:2 by , 6 years ago
Replying to johnmaddock:
Then r112 becomes 5404319552844595/54043195528445952 which is 0.0999999999999999962992565845828115319212277730305989583... in decimal, which rounds up to 0.10000000000000001 and not down to 0.099999999999999992 due to the "6" after the string of 9's.
Thank you for investigating my program, but I still don't understand this point.
To begin with, 0.099999999999999992 is actually 0.09999999999999999167332... and 0.10000000000000001 is actually 0.1000000000000000055511... (they differ by 1ulp)
So 0.09999999999999999629925... is less than their midpoint 0.09999999999999999861222...
Also, double
is a binary floating-point number. Thus I believe round-off calculation on a decimal digit "6" explains nothing here.
If I run the following program, I get 0.099999999999999992.
#include <iostream> #include <iomanip> using namespace std; int main() { double d = 0.09999999999999999629926; std::cout << std::setprecision(17) << d << std::endl; return 0; }
So isn't 0.099999999999999992 the expected result? Or am I confusing myself?
follow-up: 4 comment:3 by , 6 years ago
Milestone: | To Be Determined → Boost 1.62.0 |
---|---|
Resolution: | → fixed |
Status: | new → closed |
You're correct, I was the one confused!
Fixed in https://github.com/boostorg/multiprecision/commit/7ebd9dfd9b0bc463642b065036a3cd6947a74431.
However, your second test case still fails testing rd12, in this case I *think* the bug is yours: r12 is
14411518807585589/144115188075855872
which in binary is exactly the 54 bit quantity:
0.000 1100110011 0011001100 1100110011 0011001100 1100110011 010 1 rounded here ^
And in this case no rounding up occurs since we have a tie and round to even.
Please do correct me and re-open if I'm wrong! :)
comment:4 by , 6 years ago
Great work! Thank you very much! :)
And in this case no rounding up occurs since we have a tie and round to even.
Yes, you're right! Sorry for messing up.
I believe it's an error in your test program, taking the first example:
Note that these fractions are exact (in the binary, not decimal sense).
Then r112 becomes 5404319552844595/54043195528445952 which is 0.0999999999999999962992565845828115319212277730305989583... in decimal, which rounds up to 0.10000000000000001 and not down to 0.099999999999999992 due to the "6" after the string of 9's.