Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ways around using a double as a key in a std set/map

The problem of using doubles as keys in maps/sets is floating point precision.

Some people have suggested adding an epsilon in your compare function, but that means your keys will no longer fulfil the necessary strict weak ordering criterion. This means that you will get a different set/map depending on the order of inserting your elements.

In the case where you want to aggregate/combine/merge data based on double values, and are willing to allow a certain level of rounding/epsilon (clearly, you'll have to), is the following solution a good idea?

Convert all the doubles (where we intended as keys) into integers by multiplying them by the precision factor (e.g. 1e8) and rounding to the nearest integer (int)i+0.5(if i>0), then create a set/map that keys off these integers. When extracting the final values of the keys, divide the ints by the precision factor to get the double value back (albeit rounded).

like image 248
zhaomin Avatar asked Jun 29 '15 16:06

zhaomin


People also ask

Can a map have 2 Keys C++?

Implement a MultiKeyMap in C++A MultiKeyMap is a map that offers support for multiple keys. It is exactly the same as a normal map, except that it needs a container to store multiple keys. A simple solution to implement a MultiKeyMap in C++ is using std::pair for the key.

Can we have pair as key in map?

Do you mean cout << mymap[make_pair(1,2)] << endl; ? (1,2) is non-sensical, at least in this context. You must have an std::pair to be used as your key, and that means following what @andre just commented. Yes!

How do I know if a STD map contains a key?

To check for the existence of a particular key in the map, the standard solution is to use the public member function find() of the ordered or the unordered map container, which returns an iterator to the key-value pair if the specified key is found, or iterator to the end of the container if the specified key is not ...

What does STD map return if key not found?

std::map operator[] inserts the default constructed value type in to the map if the key provided for the lookup doesn't exist. So you will get an empty string as the result of the lookup.


2 Answers

"Convert all the doubles (where we intended as keys) into integers by multiplying them by the precision factor (e.g. 1e8) and rounding to the nearest integer (int)i+0.5(if i>0), then create a set/map that keys off these integers. When extracting the final values of the keys, divide the ints by the precision factor to get the double value back (albeit rounded)."

I would recommend using integer type keys (e.g. long long) for the map in first place, and trim them for double representation using a fixed precision for division.

But that depends, if you are able to apply fix point math for your actual use case. If you need to cover a wide range of value precisions (like e.g. +-1e-7 - +-1e7), such approach won't work.

like image 123
πάντα ῥεῖ Avatar answered Sep 22 '22 09:09

πάντα ῥεῖ


Convert all the doubles (where we intended as keys) into integers by multiplying them by the precision factor (e.g. 1e8) and rounding to the nearest integer (int)i+0.5(if i>0), then create a set/map that keys off these integers. When extracting the final values of the keys, divide the ints by the precision factor to get the double value back (albeit rounded).

Instead of dividing by the precision factor to get the doubles back, simply store the double together with the associated value in a struct, and put that struct in the dictionary as the "value" for that integer key. That way, the original double value is still around and can be used for calculations. Just not for the key search.

If, however, you can live with slightly rounded values (due to the fact you simply divide an integer by an epsilon), your suggested approach is already good enough.

As the other answer says, it very much depends on the range of the values. If some are extremely huge and others are extremely small, then your approach to get integer keys won't work. If they are only a few digits apart, then it might.

like image 43
Rudy Velthuis Avatar answered Sep 26 '22 09:09

Rudy Velthuis