Преобразование с потерями между длинными двойными и двойными

Меня удивило, что следующий код возвращает false для gcc 13 и clang 18. Почему это происходит? Разве число 8.1 не представимо в обоих форматах?

#include <iostream>
#include <iomanip>
int main()
{
    const long double val = 8.1L;
    const double val2 = static_cast<double>(val);
    const long double val3 = static_cast<long double>(val2);
    std::cout << std::boolalpha << (val == val3) << '\n';

    return 0;
}

распечатайте sizeof(double) и sizeof(long double). Если long double больше, это означает, что оно может быть более точным, в нем больше битов. Это важно, потому что .1 не представимо в базе 2, вместо этого вы получаете близкое приближение.

NathanOliver 25.07.2024 16:58

Это зависит не только от компилятора, но и от базовой среды (ОС, оборудования) и используемой стандартной библиотеки. Есть системы, в которых sizeof(double) == sizeof(long double) верно, и есть системы, в которых это ложь.

Some programmer dude 25.07.2024 16:58

Вскоре после написания вопроса я понял, что он не совсем представим и поэтому может отличаться в обоих форматах.

Martin Fehrs 25.07.2024 17:00

№ 8.1 не представим. Например, 1/3 = 0,33333.... в десятичном виде (сколько бы цифр вы ни вводили, это все равно не 1/3). То же самое для «8.1» в двоичном формате 1000.0001 1001 1001 [1001].. (группа 1001 начинает повторяться бесконечно)

Pepijn Kramer 25.07.2024 18:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Разве число 8.1 не представимо в обоих форматах?

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

Если число может быть представлено в формате с плавающей запятой, оно может быть представлено в форме ±M•be, где b — это основание, используемое для формата, а M и e — целые числа. Существуют ограничения на M и e, которые зависят от формата, и часть M часто записывается как число с фиксированной точкой, а не целое число, но это всего лишь изменение масштаба. Представление целым числом математически эквивалентно.

С основанием два 8,125 можно представить как +65•2−3, потому что 8,125•23 = 65. Однако не существует степени двойки, на которую можно было бы умножить 8,1, чтобы получить целое число. 8,1•2 = 16,2. 16,2•2 = 32,4. 32,4•2 = 64,8. 64,8•2 = 129,6. 129,6•2 = 259,2. 259,2•2 = 518,4. Вы можете увидеть цифру после зацикливания десятичной точки: .2, .4, .8, .6, .2, .4, .8, .6, .2,… Она никогда не исчезнет.

8.1 в двоичном формате — это 1000.00011001100110011001100110011001100110011001100110011…2.

Формат, обычно используемый для double, IEEE-754binary64, имеет 53 бита для своего мантисса. При преобразовании 8.1 в этот формат оно будет округлено до 53 бит. (Результат этого округления: 8,0999999999999996447286321199499070644378662109375.)

Я не знаю, какой формат используется в вашей реализации long double, но, вероятно, он имеет значительно больше 53 бит, скажем, p бит. Когда 8.1 округляется до этого формата, оно округляется до p бит. Ваша переменная long doubleval получает это округленное значение.

Когда оценивается static_cast<double>(val), это значение округляется до 53 бит, и val2 получает это значение. Это значение, отличное от val.

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