2

I am using the boost::multiprecision library for decimal float types, and wish to compare two floats to the specified precision.

However, cpp_dec_float seems to compare the number not to the specified precision, but also includes the guard digits:

#include <iostream>

#include <boost/multiprecision/cpp_dec_float.hpp>
//#include <boost/math/special_functions.hpp>

typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<50> > flp_type;

int main(int argc, char* argv[])
{
    // 50 decimal digits
    flp_type sqrt2("1.4142135623730950488016887242096980785696718753769");
    // Contains calculated guard digits
    flp_type result(boost::multiprecision::sqrt(flp_type("2")));

    // The 50 digits of precision actually ompare equal
    std::cout << std::setprecision(50) << sqrt2 << std::endl;
    std::cout << std::setprecision(50) << result << std::endl;
    // I want this to compare to the specified precision of the type, not the guard digits
    std::cout << (result==sqrt2) << std::endl;

    return 0;
}

Output:

1.4142135623730950488016887242096980785696718753769
1.4142135623730950488016887242096980785696718753769
0

Expected:

1.4142135623730950488016887242096980785696718753769
1.4142135623730950488016887242096980785696718753769
1

See on Coliru

I have tried to "truncate" with precision(), but to no avail. Is there a way to compare the two numbers without resorting to epsilon comparisons?

namezero
  • 2,203
  • 3
  • 24
  • 37

2 Answers2

2

If you strip the guard bits, you effectively cripple the fidelity of the type as intended.

A surefire way would be to use (de)serialization, really.

So I suggest

Live On Coliru

// Either
std::cout << std::numeric_limits<flp_type>::epsilon() << "\n";
std::cout << (abs(result-sqrt2) < std::numeric_limits<flp_type>::epsilon()) << std::endl;

// Or
result = flp_type { result.str(49, std::ios::fixed) };
std::cout << (result==sqrt2) << std::endl;

Note that the epsilon is 1e-49 there

Prints

1.4142135623730950488016887242096980785696718753769
1.4142135623730950488016887242096980785696718753769
1e-49
1
1

Obviously the epsilon() based comparison would be appear the more efficient

sehe
  • 374,641
  • 47
  • 450
  • 633
  • You roam every corner of SO eh? :D I was fearing your answer that epsilon is the way to go (aside of course string comparisons), but since the type is wrapped in some other logic this is mitigated. I didn't mean stripping the guard digits per se. I mean for comparison, but keeping them around for calculations. I find it counterintuitive that the type will use the guard digits for comparison, but that discussion is pointless here :) – namezero Dec 09 '14 at 17:36
  • That being said, the place to "fix" this would be the cmp_data() method in cpp_dec_float.hpp(1292) (Boost 1.56). The mismatch pair here could be investigated further to see if the mismatch is in the guard digits, and if it is, return 0. – namezero Dec 09 '14 at 17:38
  • You can take it up with the devs to find out whether this is an accident or by design. I'm not sure about that – sehe Dec 09 '14 at 19:21
  • @sehe Isn't the epsilon comparison [not recommended](https://stackoverflow.com/questions/17333/what-is-the-most-effective-way-for-float-and-double-comparison) (12th answer)? Can you explain? – algae Aug 18 '20 at 11:31
  • @algae I think there are applications that specifically require tolerance-based comparison, and there would be other applications where it would make zero sense. I have no idea what answer your try to refer to (answers are sorted differently for different people/times) but you could just link the specific answer. – sehe Aug 18 '20 at 18:23
  • Thanks @sehe. I have a similar issue with comparison of boost::multiprecision numbers and was wondering why you chose the epsilon comparison here. But I understand that it depends and that thread seems filled with contradicting examples and recommendations. The answer I tried to share was [this](https://stackoverflow.com/a/39523514/10518066) one. – algae Aug 18 '20 at 23:48
  • 1
    @algae Yeah that commenter is not very helpful, although none of the answers seems comrpehensive (didn't scan all of them). The best resource I know on the topic is [Comparing Floating Point Numbers, 2012 Edition](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/). It comes down to: _"**Know what you’re doing**/ There is no silver bullet. You have to choose wisely."_. If you don't have time to read the whole thing (yet), read that section. Also, feel free to skip the geeky bit about `sin(pi)` :) – sehe Aug 19 '20 at 11:36
  • 1
    @algae I recently made a little demo [based on `nextafter` / `nexttoward`](http://coliru.stacked-crooked.com/a/589064b79a6e1ca4) to show how relative epsilon does behave. In a sense it shows the relation between 1 ULP and relative epsilon. – sehe Aug 19 '20 at 11:41
0
bool is_equal = abs(result-sqrt2) < std::pow(10, -std::numeric_limits< flp_type >::digits10 );
Long Bu
  • 513
  • 4
  • 9