Почему double может хранить большие числа, чем unsigned long long?
вопрос в том, что я не совсем понимаю, почему double может хранить большие числа, чем unsigned long long. Поскольку оба они имеют длину 8 байт, поэтому 64 бита.
где в unsigned long long все 64 бита используются для хранения значения, с другой стороны, double имеет 1 для знака, 11 для экспоненты и 52 для мантиссы. Даже если 52 бита, которые используются для мантиссы, будут использоваться для хранения десятичных чисел без плавающей точки, у него все еще есть 63 бита ...
но LLONG_MAX значительно меньше, чем DBL_MAX ...
Почему?
6 ответов
причина в том, что unsigned long long хранит точно целые числа, а double хранит мантиссу (с ограниченной 52-битной точностью) и показатель.
в данном double для хранения очень больших чисел (около 10308), но не точно. У вас есть около 15 (почти 16) допустимых десятичных цифр в double, а остальные 308 возможных десятичных дробей-нули (на самом деле не определены, но вы можете принять "ноль" для лучшего понимания).
Ан unsigned long long только имеет 19 цифр, но каждая из них точно определена.
EDIT:
В ответ на комментарий ниже "как именно это работает", у вас есть 1 бит для знака, 11 бит для экспоненты и 52 бита на мантиссу. Мантисса имеет подразумеваемый бит" 1 " в начале, который не сохраняется, поэтому эффективно у вас есть 53 мантиссы. 253 9.007E15, поэтому у вас есть 15, почти 16 десятичных цифр для работы с.
Показатель имеет знаковый бит и может варьироваться от -1022 до +1023, который используется для масштабирования (двоичный сдвиг влево или вправо) мантиссы (21023 около 10307, следовательно, пределы диапазона), поэтому очень маленькие и очень большие числа одинаково возможны с этим форматом.
Но, конечно, все числа, которые вы можете представить, имеют столько точности, сколько поместится в Матиссу.
в целом, числа с плавающей запятой не очень интуитивно понятный, так как" простые " десятичные числа не обязательно представляются как числа с плавающей запятой. Это связано с тем, что мантисса двоичный. Например, можно (и легко) представить любое положительное целое число до нескольких миллиардов или такие числа, как 0.5, 0.25 или 0.0125, с идеальной точностью.
С другой стороны, также можно представить число, подобное 10250, но только приблизительно. На самом деле, вы найдете, что 10250 и 10250+1-это одно и то же число (подождите, что???). Это потому, что, хотя вы можете легко иметь 250 цифр, у вас не так много значительное цифры (читать " значительный "как" известный "или"определенный").
Кроме того, представляя что-то, казалось бы, простое, как 0.3 и возможно только приблизительно, хотя 0.3 даже не является "большим" числом. Однако вы не можете представить 0.3 в двоичном формате, и независимо от того, какой двоичный показатель вы прикрепляете к нему, вы не найти любое двоичное число, которое приводит к точно 0.3 (но вы можете получить очень близко).
некоторые "специальные значения" зарезервированы для " бесконечности "(как положительной, так и отрицательной), а также" не числа", поэтому у вас есть очень немного меньше, чем общий теоретический диапазон.
unsigned long long с другой стороны, никак не интерпретирует битовый шаблон. Все числа, которые можно представить просто точное число, представленное двоичным кодом. Каждая цифра каждое число точно определено, масштабирование не происходит.
значения с плавающей запятой IEEE754 могут хранить больший ряд цифр просто потому, что они жертвуют точности.
под этим я подразумеваю, что 64-разрядный интегральный тип может представлять каждое значение в своем диапазоне, но 64-разрядный двойной не может.
например, попытка сохранить 0.1 в двуспальную не дать вы 0.1, это даст вам что-то вроде:
0.100000001490116119384765625
(это на самом деле ближайший один значение точности, но тот же эффект будет применяться для двойной точности).
но, если вопрос "как вы получаете больший диапазон с меньшим количеством битов, доступных для вас?"просто некоторые из этих битов используются для масштабирования значения.
классический пример, Предположим, у вас есть четыре десятичные цифры для хранения значения. С целым числом вы можете представить числа 0000 через 9999 включительно. Точность в пределах этого диапазона идеально, вы можете представить каждое интегральное значение.
однако, пойдем с плавающей запятой и используем последнюю цифру в качестве шкалы, чтобы цифры 1234 фактически представляют число 123 x 104.
так теперь ваш диапазон от 0 (в лице 0000 через 0009) через 999,000,000,000 (в лице 9999 будучи 999 x 109).
но вы не можете представлять любое число внутри этот диапазон. Например, 123,456 невозможно представить, шкаф, который вы можете получить, с цифрами 1233, которые дают вам 123,000. И, на самом деле, где целочисленные значения имели точность четырех цифр, теперь у вас есть только три.
это в основном, как IEEE754 работает, жертвуя точностью для диапазона.
отказ от ответственности
это попытка дать простое для понимания объяснение о том, как работает кодировка с плавающей запятой. Это упрощение, и оно не охватывает никаких технических аспектов реального стандарта IEEE 754 с плавающей запятой (нормализация, нулевой знак, бесконечности, NaNs, округление и т. д.). Однако представленная здесь идея верна.
понимание того, как работают числа с плавающей запятой, серьезно затрудняется дело в том, что компьютеры работают с числами в базе 2 в то время как люди не легко справляются с ними. Я попытаюсь объяснить, как работают числа с плавающей запятой, используя base 10.
давайте построим представление числа с плавающей запятой, используя знаки и базу 10 цифры (т. е. обычные цифры из 0 to 9 мы используем на ежедневной основе).
Допустим, у нас есть 10 квадратных ячеек, и каждая ячейка может содержать либо знак (+ или -) или a десятичная цифра (0, 1, 2, 3, 4, 5, 6, 7, 8 или 9).
мы можем использовать 10 цифр для хранения целых чисел со знаком. Одна цифра для знака и 9 цифр для значения:
sign -+ +-------- 9 decimal digits -----+
v v v
+---+---+---+---+---+---+---+---+---+---+
| + | 0 | 0 | 0 | 0 | 0 | 1 | 5 | 0 | 0 |
+---+---+---+---+---+---+---+---+---+---+
вот как значение 1500 представляется в виде целого числа.
мы также можем использовать их для хранения чисел с плавающей точкой. Например, 7 цифр для мантиссы и 3 цифры для показатель:
+------ sign digits --------+
v v
+---+---+---+---+---+---+---+---+---+---+
| + | 0 | 0 | 0 | 1 | 5 | 0 | + | 0 | 1 |
+---+---+---+---+---+---+---+---+---+---+
|<-------- Mantissa ------->|<-- Exp -->|
это одно из возможных представлений 1500 как значение с плавающей запятой (используя наше представление 10 десятичных цифр).
значение мантиссы (M) is +150 значение показателя (E) составляет +1. Значение, представленное выше:
V = M * 10^E = 150 * 10^1 = 1500
диапазоны
целочисленное представление может хранить подписанные значения между -(10^9-1) (-999,999,999) и +(10^9-1) (+999,999,999). Больше, оно может представляют каждое целое значение между этими пределами. Более того, для каждого значения существует одно представление, и оно является точным.
представление с плавающей запятой может хранить подписанные значения для мантиссы (M) между -999,999 и +999,999 и для экспоненты (E) между -99 и +99.
он может хранить значения между -999,999*10^99 и +999,999*10^99. Эти цифры 105 цифры, гораздо больше, чем 9 цифры из крупнейших числа, представленные в виде целых чисел выше.
потеря точности
отметим, что для целых значений, M сохраняет знак и первые 6 цифр значения (или меньше) и E - это количество цифр, которые не вписываются в M.
V = M * 10^E
давайте попробуем представить V = +987,654,321 используя нашу кодировку с плавающей запятой.
, потому что M ограничен +999,999 он может хранить только +987,654 и E будет +3 (последние 3 цифры V не может поместиться в M).
положить их вместе:
+987,654 * 10^(+3) = +987,654,000
это не наше первоначальное значение V но лучшее приближение мы можем получить, используя это представление.
заметим, что все числа между (и включая) +987,654,000 и +987,654,999 аппроксимируются с использованием того же значения (M=+987,654, E=+3). Также нет возможности хранить десятичные цифры для чисел больше, чем +999,999.
как правило, для чисел больше максимального значения M (+999.999), этот метод создает одинаковое представление для всех значений между +999,999*10^E и +999,999*10^(E+1)-1 (целых или вещественных значения, это не имеет значения).
вывод
для больших значений (больше максимального значения M), представление с плавающей запятой имеет пробелы между числами, которые оно может представлять. Эти пробелы становятся все больше и больше по мере того, как ценность E увеличивается.
вся идея "плавающей точки" состоит в том, чтобы хранить дюжину или около того наиболее репрезентативных цифр (начало числа) и величину числа.
давайте возьмем скорость света в качестве примера. Его значение составляет около 300,000 km/s. Будучи таким массивным, для большинства практических целей вам все равно, если это 300,000.001 km/s или 300,000.326 km/s.
на самом деле, это даже не так много, лучшее приближение
299,792.458 km/s.
числа с плавающей запятой извлекают важные характеристики скорости света: его величина составляет сотни тысяч км / с (E=5) и его стоимость составляет 3 (сто тысяч км/с).
speed of light = 3*10^5 km/s
наше представление с плавающей запятой может приблизить его:299,792 km/s (M=299,792, E=0).
какая магия происходит ???
тот же вид магии, который позволяет представлять 101-значным номером
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
as
1.0 * 10100
это просто вместо базы 10 вы делаете это в базе 2:
0.57149369564113749110789177415267 * 2333.
эта нотация позволяет компактно представлять очень большие (или очень маленькие) значения. Вместо того чтобы хранить каждую цифру, вы храните significand (a.к. a. мантисса или дробь) и показатель. Таким образом, числа длиной в сотни десятичных знаков могут быть представлены в формате, который занимает всего 64 бита.
это показатель, который позволяет числам с плавающей запятой представлять такой большой ряд ценностей. Значение показателя 1024 требуется только 10 бит для хранения, но 21024 - это 308-значное число.
компромисс заключается в том, что не каждое значение может быть представлено ровно. С 64-разрядным целым числом каждое значение между 0 и 264-1 (или -263 to 263-1) имеет точное представление. Это не относится к числам с плавающей запятой по нескольким причинам. Прежде всего, у вас есть только так много битов, давая вам только так много цифр точности. Например, если у вас есть только 3 значащие цифры, то вы не можете представлять значения между 0.123 и 0.124, или 1.23 и 1.24, или 123 и 124, или 1230000 и 1240000. По мере приближения к краю диапазона разрыв между представимыми значениями увеличивается.
во-вторых, так же, как есть значения, которые не могут быть представлены в конечном количестве цифр (3/10 дает непрерывную последовательность 0.33333...10), есть значения, которые не могут быть представлены в конечном количестве битов (1/10 дает непрерывную последовательность 1.100110011001...2).
возможно, вы чувствуете, что" хранение числа в N битах " является чем-то фундаментальным, тогда как существуют различные способы сделать это. На самом деле, точнее сказать мы представляют число в N битах, поскольку значение зависит от того, какую конвенцию мы принимаем. Мы можем, в принципе, принять любое соглашение, которое нам нравится, для которого числа представляют разные N-разрядные шаблоны. Существует двоичное соглашение, используемое для unsigned long long и другие целочисленные типы, а также соглашение мантиссы + экспоненты, как используется для double, но мы могли бы также определить (абсурдное) наше собственное соглашение, в котором, например, все биты нуля означают любое огромное число, которое вы хотите указать. На практике мы обычно используем соглашения, которые позволяют нам объединять (добавлять, умножать и т. д.) номера эффективно используя оборудование, на котором мы запускаем наши программы.
тем не менее, на ваш вопрос нужно ответить, сравнив наибольшее двоичное N-разрядное число с наибольшим числом формы 2^exponent * mantissa, где exponent mantissa несколько E-и M-разрядные двоичные числа (с неявным 1 в начале мантиссы). Это 2^(2^E-1) * (2^M - 1), который, как правило, действительно намного больше, чем 2^N - 1.
маленький пример Дэймон и Paxdiablo объяснения:
#include <stdio.h>
int main(void) {
double d = 2LL<<52;
long long ll = 2LL<<52;
printf("d:%.0f ll:%lld\n", d, ll);
d++; ll++;
printf("d:%.0f ll:%lld\n", d, ll);
}
выход:
d:72057594037927936 ll:72057594037927936
d:72057594037927936 ll:72057594037927937
обе переменные были бы увеличены одинаково со сдвигом 51 или меньше.