Strange std::set comparator outcome.

Started by
1 comment, last by rip-off 8 years, 4 months ago

From: TimeAStar 1 X: 34 Z 38 total_cost 31.7885 path_cost 2.475 estimated_cost 29.3135 Current Depth: 1
From: TimeAStar 2 X: 34 Z 50 total_cost 31.7885 path_cost 8.38229 estimated_cost 23.4062 Current Depth: 3

2.475+29.3135 = 31.7885

8.38229+23.4062 = 31.78849

#2 should have a higher rank than #1 What the heck is going on?

Thanks

Jack

struct NodePoolComparator {

    bool operator()(const NodePoolNode& node1, const NodePoolNode& node2) const
    {
        return std::tie(node1.total_cost, node1.estimated_cost, node1.m_time_dependent_id)
            < std::tie(node2.total_cost, node2.estimated_cost, node2.m_time_dependent_id);
    }
};
 
Advertisement

All the numbers you listed are presented in a decimal representation, easy for humans to comprehend. But they were originally stored in binary, and so the decimal representation is almost definitely an approximation, not an exactly identical value.

The result is that when you add 2.475 and 29.3135 to get 31.7885, you're not actually doing the exact same math that the computer is doing. It is adding two numbers that are really close to 2.475 and 29.3135 and getting a result that is really close to 31.7885, but it is different enough to matter at the computational level. Similarly for the other set of numbers you presented. I wouldn't be surprised if the two additions were resulting in identical results in the binary representation, meaning that your less-than returns false no matter which way the comparison is done, and the consumer of the comparison is free to pick either one as the best. Or it's even possible that the decimal representations lose enough accuracy due to rounding errors that the second pair of numbers, when added in their binary form, actually are larger than the first pair. That is, if the first two numbers were both rounded up, and the second two numbers were both rounded down, then you might incorrectly expect the first two summed to be larger than the second two, when that's not actually the case.

In the end, it's usually best to structure your use of floating point numbers such that very minor differences of this sort don't really matter. Is it really a problem that the comparator thinks that #1 ranks higher than #2 in this case? The path finder ought to spit out nearly identically optimal paths either way.

And if you want to dive deep into some research and gain some very valuable knowledge on the subject, I'd recommend What Every Computer Scientist Should Know About Floating-Point Arithmetic and similar articles.

"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke

std::tie creates a std::tuple, when you compare std::tuples it compares the elements sequentially, so in your example first by total_cost, then by estimated_cost and finally by m_time_dependent_id:


Compares lhs and rhs lexicographically, that is, compares the first elements, if they are equivalent, compares the second elements, if those are equivalent, compares the third elements, and so on.

All comparison operators are short-circuited; they do not access tuple elements beyond what is necessary to determine the result of the comparison.

So if you want to compare the sums of the values, then just sum them! :]

But Andy Gainey's explaination might still be relevant, for example if the two apparently equal floats 31.7885 values aren't precisely equal in binary. It is good to be aware of this.

This topic is closed to new replies.

Advertisement