Je dobrý nápad použiť typ float ako kľúč pre kontajner?
sobes.tech AI
Odpoveď od AI
Použitie float ako kľúča pre asociatívne kontajnery (napríklad std::map, std::unordered_map) alebo pre priame zoradenie sa neodporúča z nasledujúcich dôvodov:
-
Problémy s porovnávaním: Rovnosť dvoch čísel typu
float(alebodouble) sa zriedka dosahuje kvôli chybám v reprezentácii s plávajúcou desatinnou čiarkou. Porovnaniea == bmôže vrátiť nepravdivý výsledok, aj keď matematicky sú čísla rovnaké. To narúša invarianty kontajnerov, ktoré vyžadujú prísny slabý poriadok (std::mapastd::set) alebo správny výpočet hash a porovnanie ekvivalencie (std::unordered_mapastd::unordered_set). -
Nesprávne zoradenie: Štandardné operátory porovnávania pre
floatnezaručujú vždy prísny slabý poriadok pre všetky možné hodnoty (napríklad NaN). -
Nestabilný hash: Implementácia hash funkcií pre
floatmôže byť nestabilná kvôli rovnakým problémom s reprezentáciou, čo môže viesť k nepredictívnemu správaniu alebo nízkej výkonnosti hash tabuliek.
Odporúčané prístupy:
-
Použitie celočíselného reprezentovania: Ak presnosť nie je kritická alebo čísla majú obmedzený rozsah a rozlíšenie, môžete škálovať a konvertovať
floatna celé číslo (napríklad,intalebolong long) a použiť ho ako kľúč.float f = 1.23f; int key = static_cast<int>(f * 100); // Príklad škálovania std::map<int, Value> my_map; my_map[key] = niektore_hodnota; -
Použitie pevného bodu: Pre prípady, keď je požadované presné zobrazenie desatinných čísel, môžete použiť knižnicu na prácu s číslami s pevným bodom.
-
Porovnávanie s toleranciou (epsilon): Hoci to neumožňuje priamo použiť
floatako kľúč, pri vyhľadávaní môžete porovnávať hodnoty s použitím malého epsilon.bool are_equal(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // Nie je vhodné na priame použitie ako kľúč v map -
Použitie vlastného porovnávacieho (pre
std::map/std::set): Môžete definovať vlastného porovnávacieho, ktorý zohľadňuje toleranciu, ale to je spojené s rizikami narušenia invariancií prísneho slabého poriadku.struct FloatComparer { bool operator()(float a, float b) const { // Jednoduchý príklad, ktorý môže narušiť prísny slabý poriadok return a < b - 1e-6; } }; // Neodporúča sa na použitie v reálnych aplikáciách // std::map<float, Value, FloatComparer> my_map; -
Použitie bitovej reprezentácie ako celého čísla (pre
std::unordered_map/std::unordered_set): Pre hash tabuľky môžete previesť bitové zobrazeniefloatna celé číslo a použiť ho ako kľúč. To zabezpečuje jedinečnosť kľúča pre každé unikátne bitové zobrazeniefloat, ale nerieši problém porovnávania plávajúcich čísel, ktoré sú matematicky rovnaké, ale majú odlišné bitové zobrazenie (napríklad 0.0 a -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 { // Porovnanie bitových zobrazení 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; // Poznámka: toto porovnáva bitové zobrazenia, nie matematickú rovnosť
Vo väčšine prípadov, ak sú kľúče čísla s plávajúcou desatinnou čiarkou, je vhodné prehodnotiť štruktúru dát alebo použiť jeden z prístupov s celočíselným alebo fixným bodovým zobrazením.