Почему double в C печатает меньше десятичных цифр, чем C++? - программирование

Почему double в C печатает меньше десятичных цифр, чем C++?

У меня этот код на C, где я объявлял 0.1 как double.

#include <stdio.h> 
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

Это то, что он печатает, a is 0.10000000000000001000000000000000000000000000000000000000

Тот же код в C++,

#include <iostream>
using namespace std;
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

Это то, что он печатает, a is 0.1000000000000000055511151231257827021181583404541015625

В чем разница? Когда я читаю, оба выделены 8 байтами? Как C++ печатает больше цифр в десятичных знаках?

Также, как он может идти до 55 знаков после запятой? Плавающая точка IEEE 754 имеет только 52 бит для дробного числа, с помощью которых мы можем получить 15 десятичных цифр точности. Он хранится в двоичном формате. Почему его десятичная интерпретация хранит больше?

4b9b3361

Ответ 1

С MinGW g++ (и gcc) 7.3.0 ваши результаты воспроизводятся точно.

Это довольно странный случай Undefined Behavior.

Неопределенное поведение происходит из-за использования printf без включения соответствующего заголовка, "вращение" должно "в

C++ 17 §20.5.2.2

" Единица перевода должна включать заголовок только за пределами любого объявления или определения и должна включать заголовок лексически до первой ссылки в этой единице перевода ни одному из объектов, объявленных в этом заголовке. Диагностика не требуется.

В коде C++ измените значение <iostream> на <stdio.h>, чтобы получить действительный код C++, и получите тот же результат, что и в программе C.


Почему код C++ даже компилируется?

Ну, в отличие от C, в C++ стандартную библиотеку заголовок можно перетащить в любой другой заголовок. И, очевидно, с g++ заголовок <iostream> перетаскивает некоторые объявления printf. Просто не совсем правильный.

Подробности: с помощью MinGW g++ 7.3.0 декларация/определение printf зависит от символа макроса __USE_MINGW_ANSI_STDIO. По умолчанию используется только то, что <stdio.h> объявляет printf. Но когда __USE_MINGW_ANSI_STDIO определяется как логическое значение true, <stdio.h> предоставляет основное определение printf, которое вызывает __mingw_vprintf. И поскольку это происходит, заголовок <cstdio> определяет (через косвенный include) __USE_MINGW_ANSI_STDIO перед включением <stdio.h>.

Есть комментарий в <_mingw.h>, "Обратите внимание, что мы также включаем его для _GNU_SOURCE в C++, но не для случая C.".

В C++, с соответствующими версиями этого компилятора, существует эффективная разница между включением <stdio.h> и использованием printf, или включая <cstdio>, using std::printf; , и используя printf.


относительно

" Кроме того, как он может работать до 55 знаков после запятой? Плавающая точка IEEE 754 имеет только 52 бита для дробного числа, с помощью которых мы можем получить 15 десятичных цифр точности. Он хранится в двоичном формате. Как его десятичная интерпретация хранит больше?

... это просто десятичное представление, которое дольше. Цифры, превышающие точность внутреннего представления, около 15 цифр для 64-битного IEEE 754, по сути являются мусором, но их можно использовать для точного восстановления исходных бит. В какой-то момент они станут все нулями, и эта точка будет достигнута для последней цифры в вашем выводе программы C++.


1 Благодаря Дитриху Эппу за то, что он нашел эту стандартную цитату.

Ответ 2

Мне кажется, что оба случая печатают 56 десятичных цифр, поэтому вопрос технически основан на ошибочной предпосылке.

Я также вижу, что оба числа равны 0.1 пределах 52 бит точности, поэтому оба правильны.

Это приводит к вашему окончательному вопросу: "Как его десятичная интерпретация хранит больше?". Он не хранит больше десятичных знаков. double не хранит никаких десятичных знаков. Он хранит бит. Создаются десятичные числа.