質問するログイン新規登録
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1340閲覧

浮動小数点数を含んだ自作クラスをハッシュマップのキーに使いたい

wistaile

総合スコア24

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/07/21 12:18

0

0

###質問内容

浮動小数点数(double)をメンバ変数としてもつ、ベクトルクラスのような自作クラスがあります。
この自作クラスをstd::unordered_mapのKeyとして使いたいと思っています。

自作クラスをstd::unordered_mapのKeyとして使うためには、ハッシュ関数Hashと等価演算子Predを定義する必要があるようです。

Hashについてはstd::hash<T>とboost::hash_combineによる実装を考えています。

質問したいことは、Predの定義についてです。
自作クラスは浮動小数点数を含むので、等価判定はマシンイプシロンや丸め誤差などを考慮した、厳密でないものにする必要があります。
しかしながら、少しでも値が異なると、そこから得られるハッシュ値は全く別のものになります。
このような、厳密でない等価性を判定する関数をPredに指定することは適切ですか?
「テンプレートパラメータ Pred は二項述語で、テンプレート引数 Key に対する同値関係でなければならない。」とのことなので、問題ないと思うのですが...

ご教授いただけたら幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

結局、以下のようにすることにしました。

c++

1#include <boost/functional/hash.hpp> 2#include <functional> 3#include <iostream> 4#include <unordered_map> 5 6 7bool eq(double lhs, double rhs) { 8 constexpr double threshold = 1e-5; 9 return std::abs(lhs - rhs) 10 <= threshold * std::max(1., std::max(std::abs(lhs), std::abs(rhs))); 11} 12 13 14struct Vector { 15 double x; 16 double y; 17 double z; 18 19 Vector(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {} 20 21 bool operator==(const Vector& rhs) const { 22 return eq(x, rhs.x) && eq(y, rhs.y) && eq(z, rhs.z); 23 } 24 bool operator!=(const Vector& rhs) const { return !(*this == rhs); } 25}; 26 27 28double round(double value) { 29 constexpr double threshold = 1e-5; 30 double tmp; 31 tmp = value / threshold; 32 tmp = (double)(int)(tmp + 0.5); 33 return tmp * threshold; 34} 35 36 37namespace std { 38template <> 39class hash<Vector> { 40 public: 41 size_t operator()(const Vector& vec) const { 42 size_t seed = 0; 43 boost::hash_combine(seed, std::hash<double>()(round(vec.x))); 44 boost::hash_combine(seed, std::hash<double>()(round(vec.y))); 45 boost::hash_combine(seed, std::hash<double>()(round(vec.z))); 46 return seed; 47 } 48}; 49} // namespace std 50 51 52int main() { 53 Vector vec1{1., 2., 3.}; 54 Vector vec2{1.000001, 2.000001, 3.000001}; 55 Vector vec3{1.0001, 2.0001, 3.0001}; 56 57 assert(vec1 == vec2); 58 assert(vec1 != vec3); 59 60 std::unordered_map<Vector, int> umap; 61 62 umap[vec1] = 5; 63 64 assert(umap.count(vec1)); 65 assert(umap[vec1] == 5); 66 67 assert(umap.count(vec2)); 68 assert(umap[vec2] == 5); 69 70 assert(!umap.count(vec3)); 71} 72

投稿2019/07/21 14:27

wistaile

総合スコア24

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問