Sobes.tech
Middle

Kas on hea mõte kasutada float tüüpi konteineri võtmena?

sobes.tech AI

Vastus AI-lt

Ei soovitata kasutada float-i võtmena assotsiatiivsetes konteinerites (näiteks std::map, std::unordered_map) või otse sorteerimiseks järgmistel põhjustel:

  • Võrdluse probleemid: Kaks float arvu (või double) on harva võrdsed, kuna ujukoma arvutamise vead. a == b võrdlus võib anda vale tulemuse, isegi kui matemaatiliselt on nad võrdsed. See rikub konteinerite invarianse, mis nõuavad ranget nõrka järjekorda (std::map ja std::set) või korrektset hash- ja võrdluse arvutust (std::unordered_map ja std::unordered_set).

  • Vale järjestus: float võrdlusoperaatorid ei pruugi alati tagada ranget nõrka järjekorda kõigi võimalike väärtuste puhul (näiteks NaN).

  • Ebakindel hash: float hash-funktsioonide rakendamine võib olla ebastabiilne, kuna sama esitus võib põhjustada ettearvamatut käitumist või madalat jõudlust hash-tabelites:

Soovitatavad lähenemised:

  1. Kasutada täisarvulist esitamist: Kui täpsus ei ole oluline või arvud on piiratud vahemikus ja resolutsioonis, saab skaleerida ja teisendada float täisarvuks (näiteks int või long long) ning kasutada seda võtmena:

    float f = 1.23f;
    int võti = static_cast<int>(f * 100); // Skaleerimise näide
    std::map<int, Value> minu_map;
    minu_map[võti] = mingi_väärtus;
    
  2. Kasutada fikseeritud punkti: Kui on vaja täpset murdarvu esitamist, saab kasutada fikseeritud punkti arvutuskirjandust.

  3. Võrdluse tegemine lubatud vahega (epsilon): Kuigi see ei võimalda float-i otseselt võtmena kasutada, saab otsingutel võrrelda väärtusi väikese lubatud vahega (epsilon):

    bool onVõrdne(float a, float b, float epsilon = 1e-6) {
        return std::abs(a - b) < epsilon;
    }
    // Ei sobi otseseks kasutamiseks map võtmena
    
  4. Kasutada kasutaja määratletud võrdlusfunktsiooni (std::map/std::set): Võib määratleda kasutaja määratletud võrdlusfunktsiooni, mis arvestab lubatud vahega, kuid see võib ikkagi rikkuda ranget nõrka järjekorda:

    struct FloatComparer {
        bool operator()(float a, float b) const {
            // Lihtne näide, mis võib rikkuda ranget nõrka järjekorda
            return a < b - 1e-6;
        }
    };
    // Ei soovitata kasutada reaalsetes rakendustes
    // std::map<float, Value, FloatComparer> minu_map;
    
  5. Bitiväljundi teisendamine täisarvuks: std::unordered_map/std::unordered_set jaoks saab teisendada float bitiväljundi täisarvuks ja kasutada seda võtmena, mis tagab iga float unikaalse bitiväljundi, kuid ei lahenda float arvude võrdlusprobleemi, mis on matemaatiliselt võrdsed, kuid erineva bitiväljundiga (näiteks 0.0 ja -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 {
            // Bitiväljundi võrdlus
            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> minu_map;
    // Märkus: see võrdleb bitiväljundeid, mitte matemaatilist võrdsust
    

Kokkuvõttes, kui võtmed on ujukoma arvud ja nende esitus või täpsus tekitab probleeme, on soovitatav üle vaadata andmestruktuur või kasutada täisarvulist või fikseeritud punkti lähenemist.