Я написал тестовый пример:
//environment: compiler=msvc140-x64 system=win10
#include <map>
#include <iostream>
int main(int argc, char* argv[])
{
std::map<double, std::string> mapd2str;
double dval1 = 1;
mapd2str[dval1] = std::to_string(dval1);
double dval2 = 1 + 1e-6;
mapd2str[dval2] = std::to_string(dval2);
for (auto& p : mapd2str)
{
std::cout << "first = " << p.first << ", second = " << p.second << std::endl;
}
return 0;
}
вывод следующий:
first=1, second=1.000000
first=1.00001, second=1.000010
после того, как я изменил dval2 на 1 + 10e-20, результат будет следующим:
first=1, second=1.000000
Итак, если я использую double или float в качестве ключа std::map, есть ли какой-либо риск?
1 + 10e-20 равно 1, поскольку 10e-20 равно 1e-19 меньше, чем std::numeric_limits<double>::epsilon().
Да, ты можешь. Просто убедитесь, что вы понимаете ограничения чисел с плавающей запятой и что это значит. Проверка равенства для поиска ключей требует точного совпадения (что может быть проблемой). Но цикл по карте должен работать нормально. Обратите внимание: будьте осторожны при печати плавающих элементов с помощью std::cout, они не всегда печатают все цифры.
риск таков: если я не знаю точности ключа, который будет вставлен в карту, программа не запустится так, как я ожидал.
ок, спасибо Мартину Йорку
Может ли ключ быть нецифровым (std::nan(""))? Если да, то вам понадобится собственный компаратор, чтобы он был хорошо упорядочен.
@Элджей, ты такой заботливый~
Положительные и отрицательные 0,0 — это еще одна потенциальная проблемная область, потому что 0.0 == -0.0. Например, если вы начнете с пустой карты, то второе из этих назначений перезапишет первое: mapd2str[0.0] = "positive zero"; mapd2str[-0.0] = "negative zero";. В итоге на карте у вас останется только один элемент.
@tbxfreeware спасибо, я решил реализовать свой собственный компаратор для карты или набора.
@Eljay, да, NaN - это такие a значения, что a<a, a==a и a>a являются false одновременно, я даже не уверен, что map будет делать в этом случае, это UB





Это зависит от того, что вы называете «риском». std::map вполне подойдет, если в качестве клавиш используются float или double.
Риск может заключаться в том, что ключи, которые, как вы ожидаете, будут идентичными, не окажутся, а ключи, которые, как вы ожидаете, будут разными, на самом деле идентичны. 1 + 10e-20 то же самое double, что и 1.0, потому что double имеет конечную точность. Бывают и другие случаи, когда интуиция подводит. См. Не работает ли математика с плавающей запятой?подробнее. Хотя, повторяю, на карте нет проблем с плавающими числами в качестве ключей. Если вы вставите значение ключа 0.123, а затем выполните поиск значения по ключу 0.123, вы найдете правильное значение. В этом нет никакого риска.
Если вы пишете собственный компаратор, убедитесь, что он реализует строгий слабый порядок. Обычное сравнение эпсилон чисел с плавающей запятой не может этого сделать! Также inf и nan требуют осторожности при реализации компаратора.
Inf и -Inf должны подойти, потому что они хорошо упорядочены.
@Caleth, их устраивает <, но не обязательно их собственный компаратор
@ 463035818_is_not_an_ai, не могли бы вы показать мне правильный пример компаратора чисел с плавающей запятой, который имеет строгий слабый порядок?
@cfersX, ты не ответил на вопрос, нужно ли тебе уметь обрабатывать nan, а если не просто использовать встроенный <. Если да, вам нужно подумать об этом stackoverflow.com/questions/38798791/nan-comparison-rule-in-c-c
Что означает «риск»?