От новичка до гуру: Курсы программирования на CyberDuff

Может ли C++ std::numeric_limits‹float›::max() быть точно сохранен в вещественном числе с последующим сравнением?

Я знаю, что некоторые значения не могут быть легко определены в числах с плавающей запятой и являются только «аппроксимированными», из-за чего прямые сравнения «равно» часто не работают.

Можно ли точно хранить std::numeric_limits::max в вещественном числе, и будет ли этот код работать должным образом?

float myFloat = std::numeric_limits<float>::max();

//...later...
if(myFloat == std::numeric_limits<float>::max())
{
    //...myFloat hasn't changed...
}
24.02.2013

  • ideone.com/LeRcUt 25.02.2013
  • max - это функция, вы, вероятно, хотите max() там... 25.02.2013
  • Спасибо, исправил вопрос. 25.02.2013

Ответы:


1

Для данной (не NaN) переменной float, f, гарантируется, что f == f всегда истинно. Поскольку myFloat устанавливается на некоторое значение float, конечно, оно будет сравниваться с тем же самым значением.

Вы, видимо, имеете в виду такие случаи, как:

float f1 = 0.1;
float f2 = 1.0/10;
assert( f1 == f2 );

который может потерпеть неудачу, но это не потерпит неудачу:

float f1 = 0.1;
float f2 = 0.1;
assert( f1 == f2 );

Хотя значение, хранящееся в переменных, может не быть точно равным 0.1, а вместо этого может иметь другое значение, вы получите одно и то же значение для обеих переменных, и поэтому они будут сравниваться равными.

Какое бы значение numeric_limits<float>::max() ни возвращалось, это фиксированное значение, которое будет сравниваться с самим собой.

25.02.2013
  • Спасибо, это ответило на мой вопрос. Вы сказали, что гарантируется, что f == f. Не могли бы вы опубликовать, какая часть стандарта? Несмотря на это, помечен как принятый ответ, поскольку он наиболее прямо затрагивает вопрос. 25.02.2013
  • Серьезно?! Стандарт просто говорит, что operator== должно дать true, если указанное отношение истинно, и false, если оно ложно. Очевидно, что значение переменной равно самому себе, потому что по определению она содержит то же значение, что и она сама, она не может содержать более одного значения! (Это не относится к значениям NaN, которые всегда возвращают false даже по сравнению с самими собой, но значения NaN особенные) 25.02.2013
  • Но я не сравниваю его с собой. Я сравниваю результат реализованной компилятором статической функции, возвращающей значение с плавающей запятой, с более поздним выполнением той же функции, и я уже выразил незнание реализаций с плавающей запятой. Да, varA == varA гарантированно верно, но я думал, что ваш комментарий был «исходное значение с плавающей запятой» == «исходное значение с плавающей запятой» после приближений (я неправильно прочитал ваш пост). Это гарантируется стандартом? Моя ошибка, если это глупый вопрос! 25.02.2013
  • numeric_limis не реализуется компилятором, это просто библиотечная функция, которая возвращает число с плавающей запятой. Если значение уже сохранено в вещественном числе, то как его значение может измениться? каждый раз, когда вы вызываете функцию, вы возвращаете одно и то же значение. Если вы присвоите это значение float значению float, у вас останется то же самое значение. Даже если функция вернула значение, которое нельзя точно представить (которое она не представляет), например 0,1, каждый раз, когда вы ее вызываете, вы получаете одно и то же значение. 25.02.2013
  • Второй пример f1==f2 в моем ответе демонстрирует такую ​​ситуацию: двум переменным присваивается одно и то же значение, поэтому они имеют одинаковое значение. 25.02.2013
  • Я понял второй пример, но мой дополнительный вопрос больше похож на: assert((1.0f/10.0f) == (1.0f/10.0f)) — гарантирует ли стандарт, что идентичные выражения с плавающей запятой всегда будут приводить к одинаковому приближению значения (при условии, что это происходит во время одного выполнения программы)? 26.02.2013
  • Технически я думаю, что это определено реализацией, но большинство реализаций следуют IEEE 754, что говорит «да». Операции с плавающей запятой являются неточными, но не недетерминированными. 26.02.2013
  • @JaminGrey На практике вы, вероятно, обнаружите, что f==f всегда верно, а complicated_expr()==complicated_expr() очень редко ложно. Многие компиляторы намеренно (gcc -ffast-math) или случайно иногда позволяют себе выполнять математические операции с плавающей запятой с точностью, отличной от запрошенной. Например, если f содержит 0.1, то не совсем умный компилятор может оптимизировать f==f в f==0.1 и выполнить сравнение с двойной точностью (возможно, потому, что это быстрее для любой цели, о которой он больше всего заботится), поэтому в итоге он будет эквивалентно 0.1f==0.1, что неверно. 05.12.2013

  • 2

    Да.

    числовые ограничения — это шаблон класса, а max — это статический метод:

      template <class T> class numeric_limits {
      public:
      ...
      static T max() throw(); //constexpr if using C++11
      ...
      };
    

    Таким образом, для типа float вы фактически будете использовать std::numeric_limits<float>::max() и просто сравнивать два числа с плавающей запятой с одинаковым значением (если вы не работали с myFloat до сравнения). Значение из max() будет согласованным числом с плавающей запятой для вашей платформы и будет иметь эквивалентное двоичное представление самого себя.

    Основная проблема, с которой вы столкнулись бы, — это попытка сериализации и десериализации на разных платформах с другим двоичным представлением с плавающей запятой. Итак, если вы попытаетесь сериализовать свою переменную myFloat и на какой-либо другой машине вы попытаетесь сравнить результат десериализованного значения непосредственно с numeric_limits::max():

    if( myFloat == std::numeric_limits<float>::max() )
    

    Результат может больше не соответствовать действительности. Затем вам нужно будет закодировать вашу концепцию «MAX» в двоичном представлении и явно интерпретировать ее так, как вы хотите.

    25.02.2013
  • Хорошая мысль о сериализации - к счастью, это не вредит моему проекту. Спасибо за ответ. Игнорирование различий в представлении Можно ли разумно предположить, что std::numeric_limits‹float›::max() всегда будет точно представлен в разных представлениях/платформах/компиляторах, даже если они разные? 25.02.2013
  • Вопрос не имеет смысла. numeric_limits<float>::max() возвращает некоторое значение float. Каким бы ни было это значение, должно быть точно представлено, поскольку оно уже сохранено в float! Вы не можете точно сохранить 0.1 в float, но любое значение, возвращаемое numeric_limits<float>::max(), возвращает уже сохранено в float, т.е., как сказано в ответе выше, оно будет иметь эквивалентное двоичное представление для себя 25.02.2013
  • Это, безусловно, разумное предположение. Все представления с плавающей запятой имеют максимальное представимое число. Если бы это число было нечетким, оно было бы непредставимым. числа с плавающей запятой - это просто двоичные числа, как и любые другие под обложками, просто интерпретируемые по-разному в зависимости от представления. Вы не можете сильно полагаться на то, как операции повлияют на значение с плавающей запятой (если вы не знаете подробности представления), но вы можете быть уверены, что любое число равно самому себе. В этот момент вы можете проверить возвращаемое значение max, это представимое число с плавающей запятой. 25.02.2013
  • @JonathanWakely: Хороший вопрос. Это своего рода то, что я пытаюсь проверить, представления с плавающей запятой - это не предмет, который я действительно изучил. Я понимаю, о чем вы говорите: если 0,1 станет 0,09324242 или чем-то еще, в следующий раз, когда я использую 0,1, оно также станет 0,09324242. 25.02.2013

  • 3

    Да, но

    myFloat += someSmallFloat;
    

    не может изменить значение myFloat.

    Если вы хотите узнать больше, есть отличный учебник по представлениям с плавающей запятой под названием Что должен знать каждый программист об арифметике с плавающей запятой.

    24.02.2013
    Новые материалы

    Представляем Narwhal Technologies (Nrwl)
    6 декабря 2016 г. Маунтин-Вью, Калифорния С тех пор, как Виктор Савкин и я (Джефф Кросс) присоединились к команде Angular в Google на заре Angular 1, Angular продемонстрировал феноменальный..

    Путь AWS  — «Изучение машинного обучения — 10 начинающих ИИ и машинного обучения на AWS».
    Универсальный ресурсный центр для изучения искусственного интеллекта и машинного обучения. НОЛЬ или ГЕРОЙ, начните свое путешествие здесь. Получите решения и пройдите обучение у экспертов AWS...

    5 простых концепций Python, ставших сложными
    #заранее извините 1) Переменные x = 4 y = 5 Переменная в Python — это символическое представление объекта. После присвоения некоторого объекта переменной Python мы приобретаем..

    «Освоение вероятности: изучение совместной, предельной, условной вероятности и теоремы Байеса —…
    Виды вероятности: Совместная вероятность Предельная вероятность Условная вероятность Диаграмма Венна в вероятностях: В “Set Theory” мы создаем диаграмму Венна...

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..