Может ли тип ключа std::map быть двойным или плавающим?

Я написал тестовый пример:

//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, есть ли какой-либо риск?

Что означает «риск»?

Sam Varshavchik 01.03.2024 03:13

1 + 10e-20 равно 1, поскольку 10e-20 равно 1e-19 меньше, чем std::numeric_limits<double>::epsilon().

3CxEZiVlQ 01.03.2024 03:14

Да, ты можешь. Просто убедитесь, что вы понимаете ограничения чисел с плавающей запятой и что это значит. Проверка равенства для поиска ключей требует точного совпадения (что может быть проблемой). Но цикл по карте должен работать нормально. Обратите внимание: будьте осторожны при печати плавающих элементов с помощью std::cout, они не всегда печатают все цифры.

Loki Astari 01.03.2024 03:23

риск таков: если я не знаю точности ключа, который будет вставлен в карту, программа не запустится так, как я ожидал.

cfersX 01.03.2024 03:31

ок, спасибо Мартину Йорку

cfersX 01.03.2024 03:35

Может ли ключ быть нецифровым (std::nan(""))? Если да, то вам понадобится собственный компаратор, чтобы он был хорошо упорядочен.

Eljay 01.03.2024 03:48

@Элджей, ты такой заботливый~

cfersX 01.03.2024 04:24

Положительные и отрицательные 0,0 — это еще одна потенциальная проблемная область, потому что 0.0 == -0.0. Например, если вы начнете с пустой карты, то второе из этих назначений перезапишет первое: mapd2str[0.0] = "positive zero"; mapd2str[-0.0] = "negative zero";. В итоге на карте у вас останется только один элемент.

tbxfreeware 01.03.2024 07:09

@tbxfreeware спасибо, я решил реализовать свой собственный компаратор для карты или набора.

cfersX 01.03.2024 08:42

@Eljay, да, NaN - это такие a значения, что a<a, a==a и a>a являются false одновременно, я даже не уверен, что map будет делать в этом случае, это UB

Swift - Friday Pie 01.03.2024 15:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
10
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это зависит от того, что вы называете «риском». std::map вполне подойдет, если в качестве клавиш используются float или double.

Риск может заключаться в том, что ключи, которые, как вы ожидаете, будут идентичными, не окажутся, а ключи, которые, как вы ожидаете, будут разными, на самом деле идентичны. 1 + 10e-20 то же самое double, что и 1.0, потому что double имеет конечную точность. Бывают и другие случаи, когда интуиция подводит. См. Не работает ли математика с плавающей запятой?подробнее. Хотя, повторяю, на карте нет проблем с плавающими числами в качестве ключей. Если вы вставите значение ключа 0.123, а затем выполните поиск значения по ключу 0.123, вы найдете правильное значение. В этом нет никакого риска.

Если вы пишете собственный компаратор, убедитесь, что он реализует строгий слабый порядок. Обычное сравнение эпсилон чисел с плавающей запятой не может этого сделать! Также inf и nan требуют осторожности при реализации компаратора.

Inf и -Inf должны подойти, потому что они хорошо упорядочены.

Caleth 01.03.2024 10:51

@Caleth, их устраивает <, но не обязательно их собственный компаратор

463035818_is_not_an_ai 01.03.2024 10:52

@ 463035818_is_not_an_ai, не могли бы вы показать мне правильный пример компаратора чисел с плавающей запятой, который имеет строгий слабый порядок?

cfersX 02.03.2024 03:02

@cfersX, ты не ответил на вопрос, нужно ли тебе уметь обрабатывать nan, а если не просто использовать встроенный <. Если да, вам нужно подумать об этом stackoverflow.com/questions/38798791/nan-comparison-rule-in-‌​c-c

463035818_is_not_an_ai 06.03.2024 12:09

Другие вопросы по теме