კონტეინერის გასაღებად 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-ის ჰეშ-ფუნქციების განხორციელება შეიძლება იყოს დაუცველი იმავე წარდგენის პრობლემების გამო, რაც გამოიწვევს გაუთვალისწინებელ ქცევას ან დაბალ ეფექტიანობას ჰეშ-ტაბლებისთვის:
შეფასებული მიდგომები:
-
მთლიანი რიცხვის წარმოდგენის გამოყენება: თუ სიზუსტე არ არის მნიშვნელოვანი ან რიცხვები აქვთ შეზღუდული დიაპაზონი და განკუთვნილობა, შეიძლება масштабირება და
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; -
ფიქსირებული წერტილი: შემთხვევებში, როდესაც საჭიროა ზუსტი წილადი რიცხვების წარმოდგენა, შეიძლება გამოიყენოთ ფიქსირებული წერტილის ბიბლიოთეკა.
-
შედარება დაშვებით (epsilon): მიუხედავად იმისა, რომ ეს პირდაპირ არ იძლევა
float-ის გამოყენებას როგორც გასაღებს, ძიების დროს შეიძლება შედარება მოხდეს მცირე დაშვებით (epsilon):bool are_equal(float a, float b, float epsilon = 1e-6) { return std::abs(a - b) < epsilon; } // არ არის რეკომენდებული პირდაპირი გამოყენებისთვის -
მომხმარებლის განსაზღვრული შედარების გამოყენება (
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; -
ბიტური წარმოდგენის მთლიანი რიცხვი:
std::unordered_map/std::unordered_set-ისთვის შეიძლება ბიტური წარმოდგენის გადაყვანა მთლიანი რიცხვში და მისი გამოყენება როგორც გასაღები, რაც უზრუნველყოფს უნიკალურობას თითოეული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; // შენიშვნა: ეს შედარება ბიტური წარმოდგენების მიხედვით არის, არა მათემატიკური თანასწორობა
საერთო ჯამში, თუ გასაღებები ფლოტის რიცხვებია და მათ აქვთ პრობლემები წარდგენის ან სიზუსტის გამო, რეკომენდებულია კონტეინერის სტრუქტურის გადახედვა ან გამოყენება მთლიანი ან ფიქსირებული წერტილის მეთოდები.