Sobes.tech
Middle

Vai ir laba ideja izmantot float tipu kā atslēgu konteineram?

sobes.tech AI

Atbilde no AI

Nepieciešams izmantot float kā atslēgu asociatīvos konteineros (piemēram, std::map, std::unordered_map) vai tieši kārtošanai, tas nav ieteicams šādu iemeslu dēļ:

  • Salīdzināšanas problēmas: Divi float skaitļi (vai double) reti ir vienādi, jo pludiņa punktu pārstāvības kļūdas dēļ. a == b salīdzinājums var dot nepareizu rezultātu, pat ja matemātiski tie ir vienādi. Tas pārkāpj konteineru invarianus, kas pieprasa stingru vājo kārtību (std::map un std::set) vai pareizu haša un vienlīdzības aprēķinu (std::unordered_map un std::unordered_set).

  • Nepareiza kārtošana: float salīdzināšanas operatori ne vienmēr nodrošina stingru vājo kārtību visām iespējamām vērtībām (piemēram, NaN).

  • Nestabils hašs: float haša funkciju realizācija var būt nestabila, jo tā pati pārstāvība var radīt neparedzamu uzvedību vai zemu veiktspēju haša tabulās:

Ieteicamās pieejas:

  1. Izmantot veselos skaitļus: Ja precizitāte nav būtiska vai skaitļiem ir ierobežots diapazons un rezolūcija, var mēģināt mērogošanu un pārveidot float uz veselo skaitli (piemēram, int vai long long) un izmantot to kā atslēgu:

    float f = 1.23f;
    int atslēga = static_cast<int>(f * 100); // Mērogošanas piemērs
    std::map<int, Value> mana_karte;
    mana_karte[atslēga] = kāda_vērtība;
    
  2. Izmantot fiksētu punktu: Ja nepieciešama precīza daļskaitļu pārstāvība, var izmantot fiksēta punkta skaitļu bibliotēku.

  3. Salīdzināt ar pieļaujamu novirzi (epsilon): Lai gan tas tieši neļauj izmantot float kā atslēgu, meklēšanas laikā var salīdzināt vērtības ar mazu pieļaujamu novirzi:

    bool irVienādi(float a, float b, float epsilon = 1e-6) {
        return std::abs(a - b) < epsilon;
    }
    // Nav ieteicams tieši izmantot kā atslēgu mapē
    
  4. Izmantot lietotāja definētu salīdzinātāju (std::map/std::set): Var definēt lietotāja salīdzinātāju, kas ņem vērā pieļaujamo novirzi, bet tas joprojām var pārkāpt stingru vājo kārtību:

    struct FloatComparer {
        bool operator()(float a, float b) const {
            // Vienkāršs piemērs, kas var pārkāpt stingru kārtību
            return a < b - 1e-6;
        }
    };
    // Nav ieteicams lietošanai reālos projektos
    // std::map<float, Value, FloatComparer> mana_karte;
    
  5. Bitu pārstāvuma konvertēšana uz veselo skaitli: std::unordered_map/std::unordered_set gadījumā var pārveidot float bitu pārstāvumu uz veselo skaitli un izmantot to kā atslēgu, nodrošinot unikālu atslēgu katram float unikālajam bitu pārstāvumam, bet tas neizskauž float skaitļu salīdzināšanas problēmas, kuri ir matemātiski vienādi, bet atšķiras ar bitu pārstāvumu (piemēram, 0.0 un -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 {
            // Bitu pārstāvumu salīdzināšana
            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> mana_karte;
    // Piezīme: tas salīdzina bitu pārstāvjus, ne matemātisko vienādību
    

Kopsavilkumā, ja atslēgas ir pludiņa skaitļi un to pārstāvība vai precizitāte rada problēmas, ieteicams pārskatīt datu struktūru vai izmantot veselos skaitļus vai fiksēta punkta metodes.