Если двойная точность не гарантирует более 16 значащих десятичных цифр, как такой вывод генерируется этой стандартной программой C ++? Кроме того, небольшие операции по изменению значений, выполняемые на «ans», таких как ++ ans, не влияют на вывод на экран. Является ли ответ «вычисленным» непосредственно перед печатью результата вместо того, чтобы быть сохраненным в формате двойной точности IEEE754 в «ans» сразу?
Повторяю вопрос — почему такие цифры мусора, которые следуют за первыми 16 цифрами, дают правильные результаты для степеней 2?
#include <cstdio>
#include <cmath>
using namespace std;
int main() {
double ans = pow(2.0, 700.0);
printf("%.0f\n", ans);
return 0;
}
5260135901548373507240989882880128665550339802823173859498280903068732154297080822113666536277588451226982968856178217713019432250183803863127814770651880849955223671128444598191663757884322717271293251735781376
Решено: также нашел объяснение Вот.
Спасибо за все ответы!
По факту, 2**700
может быть точно представлен в двойном IEEE, потому что он имеет 11 битов для экспонентной части, и поэтому он может быть точно напечатан, и некоторые компиляторы генерируют такой код. Но 2**700 + 1
конечно не может быть представлен точно, и это округляет до 2**700
,
И, конечно, этот трюк работает только с степенями 2 и только с целочисленными степенями до 1023 (экспонента — это число со знаком).
У вас все еще есть только около 16 значащих десятичных цифр; это означает, что только первые 16 или около того цифр на выходе являются правильными; и все оставшиеся цифры, скорее всего, совершенно неверны и на самом деле не хранятся как часть числа с двойной точностью. Вот почему добавление 1 к числу ничего не делает — это изменение одного из незначительный цифры, которые на самом деле не записаны.
В IEEE double
представления, значение кодируется с использованием бита для отслеживания знака + или — мантиссы, которая кодирует число от 1 до 1 + 2 ^ 52-1 / 2 ^ 52 с шагом 1/2 ^ 52, и 11-битный экспонента, кодирующая множитель между 2 ^ -1031 и 2 ^ 1032. Цитировать Википедию:
Между 2 ^ 52 = 4,503,599,627,370,496 и 2 ^ 53 = 9,007,199,254,740,992 представимые числа являются в точности целыми числами. Для следующего диапазона, от 2 ^ 53 до 2 ^ 54, все умножается на 2, поэтому представимые числа являются четными и т. Д.
Итак, к тому времени, когда вы пытаетесь сохранить 2 ^ 700 в double
точное значение двойного будет увеличиваться на 2 ^ 648 каждый раз, когда мантисса увеличивается до следующего представимого значения. Чтобы «сохранить» любое значение, которое не является одним из этих точных значений, вы должны пойти на компромисс и округлить / усечь до ближайшего точно представимого значения. Поэтому, когда вы просите напечатать число без десятичных разрядов, но со всеми цифрами, компилятор может сообщить вам точное значение, представляющее, даже если оно отличается от числа, которое вы просили его сохранить … для 7 ^ 297 сохранено было после округления. Однако для 2 ^ 700 округление не требуется … мантисса 1 и показатель 700 кодируют его точно.
Я хочу сказать, что дополнительные цифры не обязательно являются произвольным мусором — хорошая реализация printf должна отображать то, что действительно является частью значения double
и потенциально значимым, если число не было округлено при сохранении.
IEEE754 дает от 15 до 17 значащих десятичных цифр в соответствии с Википедией.
Я скомпилировал это на моей машине Windows с gcc 4.8.1, и он выдает 986547236173443280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000
Для меня с флагами -std = c ++ 11 -O2 -Wall -pedantic, g ++ произведено
fld qword [ 0x405070 ]
fstp qword [ esp + 0x4 ]
call 0x403648 <printf>
Что, к сожалению, немного выходит за пределы моей зоны комфорта, но похоже, что он просто загружает дабл и бросает его в стек. Похоже, @AntonSavin прав, двойник печатается с наилучшей способностью после округления, что оказывается правильным, и поэтому ответ + 1 не меняется.