Sobes.tech
Middle

Արդյոք լավ գաղափար է օգտագործել float տիպը որպես բանալի կոնտեյների համար?

sobes.tech AI

Պատասխան AI-ից

float-ը որպես բանալի օգտագործելը ասոցացվող կոնտեյներների համար (օրինակ՝ std::map, std::unordered_map) կամ ուղղակիորեն դասավորելու համար չի խորհուրդ տրվում հետևյալ պատճառներով:

  • Համեմատության խնդիրներ: Երկու float թվերի հավասարությունը (կամ double) հազվադեպ է, քանի որ տպագրության սխալների պատճառով: a == b համեմատությունը կարող է տալ կեղծ արդյունք, նույնիսկ եթե թվաբանականորեն հավասար են: Սա խախտում է կոնտեյների ինվարիանտները, որոնք պահանջում են խիստ թույլ թույլ դասավորություն (std::map և std::set) կամ ճիշտ հեշի և հավասարության համեմատության հաշվարկ (std::unordered_map և std::unordered_set

  • Սխալ դասավորություն: float-ի համեմատական օպերատորները միշտ չէ, որ ապահովում են խիստ թույլ թույլ դասավորություն բոլոր հնարավոր արժեքների համար (օրինակ՝ NaN):

  • Անստաբիլ հեշ: float-ի հեշ ֆունկցիաների իրականացմանը կարող է խանգարել նույն տպագրության խնդիրները, ինչը կարող է հանգեցնել անպատասխանատու վարքագծի կամ ցածր արդյունավետության հեշ-թաղանթների համար:

Առաջարկվող մոտեցումներ:

  1. Օգտագործել ամբողջական ներկայացում: Եթե ճշգրտությունը կարևոր չէ կամ թվերի սահմանափակ տիրույթ և թույլատրելիություն կա, կարելի է մծկել և փոխարկել float-ը ամբողջական թիվ (օրինակ՝ int կամ long long) և օգտագործել որպես բանալի:

    float f = 1.23f;
    int key = static_cast<int>(f * 100); // Մծկումի օրինակ
    std::map<int, Value> my_map;
    my_map[key] = some_value;
    
  2. Օգտագործել ֆիքսված կետ: Եթե անհրաժեշտ է ճշգրիտ տպագրություն, կարելի է օգտագործել ֆիքսված կետի համար նախատեսված գրադարան:

  3. Համեմատել թույլատրելիության (epsilon) միջոցով: Չնայած սա չի թույլատրում օգտագործել float-ը որպես բանալի ուղղակիորեն, որոնումների ժամանակ կարելի է համեմատել արժեքները փոքր թույլատրելիությամբ:

    bool are_equal(float a, float b, float epsilon = 1e-6) {
        return std::abs(a - b) < epsilon;
    }
    // Չի հարմար է ուղղակի օգտագործելու համար որպես բանալի map-ում
    
  4. Օգտագործել օգտագործողի սահմանած համեմատիչ (std::map/std::set): Կարող եք սահմանել օգտագործողի սահմանած համեմատիչ, որը հաշվի է առնում թույլատրելիությունը, բայց դա դեռ կարող է խախտել խիստ թույլ դասավորության ինվարիանտները:

    struct FloatComparer {
        bool operator()(float a, float b) const {
            // Պարզ օրինակ, որը կարող է խախտել խիստ թույլ դասավորությունը
            return a < b - 1e-6;
        }
    };
    // Չի խորհուրդ տրվում ուղղակի օգտագործել իրական ծրագրերում
    // std::map<float, Value, FloatComparer> my_map;
    
  5. Օգտագործել битային ներկայացում ամբողջական թվի: std::unordered_map/std::unordered_set-ի համար կարելի է փոխարկել float-ի բիթային ներկայացումը ամբողջական թվի և օգտագործել որպես բանալի, ինչը ապահովում է յուրահատուկ բանալի յուրաքանչյուր float-ի բիթային ներկայացման համար, բայց չի լուծում float-ների համեմատության խնդիրները, որոնք մաթեմատիկորեն հավասար են, բայց ունեն տարբեր բիթային ներկայացում (օրինակ՝ 0.0 և -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 {
            // Բիթային ներկայացումների համեմատություն
            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;
    // Նշում՝ սա համեմատում է բիթային ներկայացումները, ոչ մաթեմատիկական հավասարությունը
    

Եզրափակելով, եթե բանալիները թվեր են, որոնք ունեն տպագրության խնդիրներ կամ սահմանափակ ճշգրտություն, խորհուրդ է տրվում rethink կոնտեյների կառուցվածքը կամ օգտագործել մեկ այլ մոտեցում՝ ամբողջական կամ ֆիքսված կետի թվեր։