Da li je dobra ideja koristiti tip float kao ključ za kontejner?
sobes.tech АИ
Одговор од АИ
Користење 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] = нека вредност; -
Користите фиксну тачку: За случајеве када је потребно тачно представљање децималних бројева, можете користити библиотеку за рад са бројевима са фиксном тачком.
-
Поређење са толеранцијом (epsilon): Иако то не дозвољава директно коришћење
floatкао кључ, при претрази можете поређивати вредности користећи мали допуштени праг (epsilon).bool are_equal(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // Не одговара за директно коришћење као кључ у мапи -
Користите кориснички дефинисан компаратор (за
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; -
Користите битну репрезентацију као целу број: За хеш табеле можете претворити битну репрезентацију
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; // Обратите пажњу: ово пореди битне приказе, не математичку равност
У већини случајева, ако су кључеви бројеви са плавајућом тачком, треба преиспитати структуру података или користити један од приступа са целобројном или фиксном тачком репрезентацијом.