Jó ötlet a float típus használata kulcsként egy tárolóhoz?
sobes.tech MI
Válasz az MI-től
Az float használata kulcsként asszociatív tárolókban (például std::map, std::unordered_map) vagy közvetlen rendezéshez nem ajánlott a következő okok miatt:
-
Összehasonlítási problémák: Két
float(vagydouble) típusú szám egyenlősége ritkán érhető el a lebegőpontos ábrázolási hibák miatt. Aza == bösszehasonlítás hamis eredményt adhat, még akkor is, ha matematikailag a számok egyenlőek. Ez sérti a konténerek szigorú gyenge sorrend invariánsait (std::mapésstd::set) vagy a hash és az egyenlőség összehasonlás helyességét (std::unordered_mapésstd::unordered_set). -
Hibás rendezés: A
floatalapértelmezett összehasonlító operátorai nem biztosítanak minden lehetséges értékre szigorú gyenge sorrendet (például NaN). -
Instabil hash: A
float-hoz készült hash függvények implementációja instabil lehet ugyanazon problémák miatt, ami kiszámíthatatlan viselkedést vagy alacsony hash-táblázati teljesítményt eredményezhet.
Ajánlott megközelítések:
-
Egész szám reprezentáció használata: Ha a pontosság nem kritikus, vagy a számok korlátozott tartományban és felbontással rendelkeznek, akkor a
float-ot skálázással és átalakítással egész számmá (példáulintvagylong long) lehet alakítani, és kulcsként használni.float f = 1.23f; int key = static_cast<int>(f * 100); // Skálázási példa std::map<int, Value> my_map; my_map[key] = some_value; -
Fix pont használata: Olyan esetekben, amikor pontos tizedes szám ábrázolás szükséges, használhatunk fix pont számokat kezelő könyvtárakat.
-
Epsilon-alapú összehasonlítás: Bár ez nem teszi lehetővé a
floatközvetlen kulcsként való használatát, kereséskor összehasonlíthatjuk az értékeket egy kis epsilon értékkel.bool are_equal(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // Nem alkalmas közvetlenül kulcsként való használatra map-ben -
Felhasználó által definiált összehasonlító (pl.
std::map/std::setesetén): Létrehozhatunk olyan összehasonlítót, amely figyelembe veszi az epsilon-t, de ez kockázatokat rejt magában a szigorú gyenge sorrend invariánsainak megsértése miatt.struct FloatComparer { bool operator()(float a, float b) const { // Egyszerű példa, ami megsértheti a szigorú gyenge sorrendet return a < b - 1e-6; } }; // Nem ajánlott közvetlenül használni valós alkalmazásokban // std::map<float, Value, FloatComparer> my_map; -
Bit reprezentáció egész számként való használata (pl.
std::unordered_map/std::unordered_set): Hash táblák esetén afloatbit reprezentációját egész számmá lehet alakítani, és kulcsként használni. Ez biztosítja, hogy minden egyedifloatbit reprezentációhoz egyedi kulcs tartozik, de nem oldja meg a lebegőpontos számok összehasonlításának problémáját, amelyek matematikailag egyenlők lehetnek, de különböző bit reprezentációval rendelkeznek (pl. 0.0 és -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 { // Bit reprezentációk összehasonlítása 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; // Figyelem: ez a bit reprezentációkat hasonlítja össze, nem a matematikai egyenlőséget
Általánosságban, ha lebegőpontos számokat használnak kulcsként, érdemes átgondolni az adatszerkezetet, vagy az egyik fent említett megközelítést alkalmazni egész szám vagy fix pont reprezentációval.