Ar gerai naudoti float tipą kaip raktą konteineriui?
sobes.tech AI
Atsakymas iš AI
Naudoti float kaip raktą asociatyviuose konteineriuose (pvz., std::map, std::unordered_map) arba tiesiogiai rūšiavimui nerekomenduojama dėl šių priežasčių:
-
Lyginimo problemos: Du
floatskaičiai (arbadouble) retai būna lygūs dėl plaukiojančio taško atstojimo klaidų.a == bpalyginimas gali duoti klaidingą rezultatą, net jei matematiškai jie yra lygūs. Tai pažeidžia konteinerių invarianus, kurie reikalauja griežto silpnojo tvarkos (std::mapirstd::set) arba teisingo maišos ir lyginimo (std::unordered_mapirstd::unordered_set) skaičiavimo. -
Netinkamas rūšiavimas:
floatpalyginimo operatoriai ne visada užtikrina griežtą silpnąją tvarką visoms galimoms reikšmėms (pvz., NaN). -
Nestabilus maišas:
floatmaišos funkcijų įgyvendinimas gali būti nestabilus dėl tų pačių atstojimo klaidų, kas gali sukelti nenuspėjamą elgesį arba žemą našumą maišos lentelėse:
Rekomenduojami būdai:
-
Naudoti sveikųjų skaičių atvaizdavimą: Jei tikslumas nėra svarbus arba skaičiai turi ribotą diapazoną ir rezoliuciją, galima mastelio keisti ir konvertuoti
floatį sveikąjį skaičių (pvz.,intarbalong long) ir naudoti jį kaip raktą:float f = 1.23f; int raktas = static_cast<int>(f * 100); // Mastelio keitimo pavyzdys std::map<int, Value> mano_map; mano_map[raktas] = kažkoks_reikšmė; -
Naudoti fiksuotą tašką: Kai reikalingas tikslius trupmeninius skaičius, galima naudoti fiksuoto taško skaičių biblioteką.
-
Lyginti su leistinu nuokrypiu (epsilon): Nors tai tiesiogiai neleidžia naudoti
floatkaip rakto, paieškos metu galima lyginti reikšmes naudojant mažą leistiną nuokrypį (epsilon):bool yraLygu(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // Netinka tiesioginiam naudojimui kaip rakto map'e -
Naudoti naudotojo apibrėžtą palyginimo funkciją (
std::map/std::set): Galima apibrėžti naudotojo palyginimo funkciją, kuri atsižvelgia į leistiną nuokrypį, tačiau tai vis tiek gali pažeisti griežto silpnojo tvarkos invarianus:struct FloatComparer { bool operator()(float a, float b) const { // Paprastas pavyzdys, kuris gali pažeisti griežtą silpnąją tvarką return a < b - 1e-6; } }; // Nerekomenduojama naudoti realiuose projektuose // std::map<float, Value, FloatComparer> mano_map; -
Bitų atvaizdžio konvertavimas į sveikąjį skaičių:
std::unordered_map/std::unordered_setgali naudotifloatbitų atvaizdį kaip raktą, konvertuojant jį į sveikąjį skaičių, užtikrinant unikalumą, bet nepašalina problemų sufloatlyginimu, kai matematiškai lygūs skaičiai turi skirtingą bitų atvaizdą (pvz., 0.0 ir -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ų atvaizdžių palyginimas 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> mano_map; // Pastaba: tai lygina bitų atvaizdus, ne matematinį lygumą
Apibendrinant, jei raktai yra plaukiojantys skaičiai ir jų atvaizdavimas ar tikslumas kelia problemų, rekomenduojama peržiūrėti duomenų struktūrą arba naudoti sveikųjų skaičių arba fiksuoto taško metodus.