Արդյոք լավ գաղափար է օգտագործել float տիպը որպես բանալի կոնտեյների համար?
sobes.tech AI
Պատասխան AI-ից
float-ը որպես բանալի օգտագործելը ասոցացվող կոնտեյներների համար (օրինակ՝ std::map, std::unordered_map) կամ ուղղակիորեն դասավորելու համար չի խորհուրդ տրվում հետևյալ պատճառներով:
-
Համեմատության խնդիրներ: Երկու
floatթվերի հավասարությունը (կամdouble) հազվադեպ է, քանի որ տպագրության սխալների պատճառով:a == bհամեմատությունը կարող է տալ կեղծ արդյունք, նույնիսկ եթե թվաբանականորեն հավասար են: Սա խախտում է կոնտեյների ինվարիանտները, որոնք պահանջում են խիստ թույլ թույլ դասավորություն (std::mapևstd::set) կամ ճիշտ հեշի և հավասարության համեմատության հաշվարկ (std::unordered_mapևstd::unordered_set)։ -
Սխալ դասավորություն:
float-ի համեմատական օպերատորները միշտ չէ, որ ապահովում են խիստ թույլ թույլ դասավորություն բոլոր հնարավոր արժեքների համար (օրինակ՝ NaN): -
Անստաբիլ հեշ:
float-ի հեշ ֆունկցիաների իրականացմանը կարող է խանգարել նույն տպագրության խնդիրները, ինչը կարող է հանգեցնել անպատասխանատու վարքագծի կամ ցածր արդյունավետության հեշ-թաղանթների համար:
Առաջարկվող մոտեցումներ:
-
Օգտագործել ամբողջական ներկայացում: Եթե ճշգրտությունը կարևոր չէ կամ թվերի սահմանափակ տիրույթ և թույլատրելիություն կա, կարելի է մծկել և փոխարկել
float-ը ամբողջական թիվ (օրինակ՝intկամlong long) և օգտագործել որպես բանալի:float f = 1.23f; int key = static_cast<int>(f * 100); // Մծկումի օրինակ std::map<int, Value> my_map; my_map[key] = some_value; -
Օգտագործել ֆիքսված կետ: Եթե անհրաժեշտ է ճշգրիտ տպագրություն, կարելի է օգտագործել ֆիքսված կետի համար նախատեսված գրադարան:
-
Համեմատել թույլատրելիության (epsilon) միջոցով: Չնայած սա չի թույլատրում օգտագործել
float-ը որպես բանալի ուղղակիորեն, որոնումների ժամանակ կարելի է համեմատել արժեքները փոքր թույլատրելիությամբ:bool are_equal(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // Չի հարմար է ուղղակի օգտագործելու համար որպես բանալի map-ում -
Օգտագործել օգտագործողի սահմանած համեմատիչ (
std::map/std::set): Կարող եք սահմանել օգտագործողի սահմանած համեմատիչ, որը հաշվի է առնում թույլատրելիությունը, բայց դա դեռ կարող է խախտել խիստ թույլ դասավորության ինվարիանտները:struct FloatComparer { bool operator()(float a, float b) const { // Պարզ օրինակ, որը կարող է խախտել խիստ թույլ դասավորությունը return a < b - 1e-6; } }; // Չի խորհուրդ տրվում ուղղակի օգտագործել իրական ծրագրերում // std::map<float, Value, FloatComparer> my_map; -
Օգտագործել битային ներկայացում ամբողջական թվի:
std::unordered_map/std::unordered_set-ի համար կարելի է փոխարկելfloat-ի բիթային ներկայացումը ամբողջական թվի և օգտագործել որպես բանալի, ինչը ապահովում է յուրահատուկ բանալի յուրաքանչյուրfloat-ի բիթային ներկայացման համար, բայց չի լուծումfloat-ների համեմատության խնդիրները, որոնք մաթեմատիկորեն հավասար են, բայց ունեն տարբեր բիթային ներկայացում (օրինակ՝ 0.0 և -0.0):union FloatIntConverter { float f; int i; }; struct FloatHash { size_t operator()(float f) const { FloatIntConverter converter; converter.f = f; return std::hash<int>()(converter.i); } }; struct FloatEqual { bool operator()(float a, float b) const { // Բիթային ներկայացումների համեմատություն FloatIntConverter converter_a, converter_b; converter_a.f = a; converter_b.f = b; return converter_a.i == converter_b.i; } }; // std::unordered_map<float, Value, FloatHash, FloatEqual> my_map; // Նշում՝ սա համեմատում է բիթային ներկայացումները, ոչ մաթեմատիկական հավասարությունը
Եզրափակելով, եթե բանալիները թվեր են, որոնք ունեն տպագրության խնդիրներ կամ սահմանափակ ճշգրտություն, խորհուրդ է տրվում rethink կոնտեյների կառուցվածքը կամ օգտագործել մեկ այլ մոտեցում՝ ամբողջական կամ ֆիքսված կետի թվեր։