C ++ ‘s std::numeric_limits<float>::digits10
, описывается на cppref как таковой:
Значение
std::numeric_limits<T>::digits10
это число из 10 цифр с основанием, которое может быть представлено типом T без изменений, то есть любое число с таким количеством десятичных цифр может быть преобразовано в значение типа T и обратно в десятичную форму без изменения из-за округления или переполнение.
Аналогичное описание существует для двоюродного брата FLT_DIG.
Значение дано:
float FLT_DIG /* 6 for IEEE float */
тем не мение, это показано здесь на С.О. что все целые числа до 16,777,216
(224) точно представимы в 32-битном типе IEEE с плавающей точкой. И если я могу посчитать, это число 8 цифры, поэтому значение для digits10
должно быть на самом деле 7, сейчас не так ли?
Скорее всего, я что-то неправильно понимаю digits10
здесь, так что же это на самом деле скажи мне?
Практическая применимость:
Меня спросили, можем ли мы сохранить все числа из 0.00
— 86,400.00
точно в 32-битном IEEE поплавке.
Теперь я очень уверен, что мы могли бы хранить все номера из 0
— 8,640,000
в 32-разрядном формате с плавающей запятой IEEE, но сохраняется ли это для того же «целочисленного» диапазона, смещенного на 2 цифры влево?
(Ограничив этот ответ IEEE754 float
).
8.589973e9
а также 8.589974e9
обе карты в 8589973504
, Это контрпример к утверждению, что седьмая значащая цифра сохраняется.
Так как на шестом значимом рисунке такого контрпримера не существует, std::numeric_limits<float>::digits10
а также FLT_DIG
6
Действительно целые числа могут быть представлены в точности до 24го сила 2. (16,777,216
а также 16,777,217
обе карты в 16,777,216
). Это потому что float
имеет 24 бит мантисса.
Поскольку другой ответ и комментарий устанавливает, digits10
охватывает все «диапазоны показателей», то есть 1234567
а также для 1.234567
а также 12345670000
— и это касается только 6 цифры!
Пример счетчика для 7 цифр:
8.589,973 e9
против 8.589,974 e9
(из примера cppref)Другая точка зрения:
Рассмотрим между каждой парой степеней-2, а float
лайк Бинарный IEEE кодирует 223 Значения распределены линейно.
Пример: между 20 и 21 или 1,0 и 2,0,
Разница между float
значения 1,0 / 223 или 10.192e-06.
Написано в текстовой форме «1.dddddd», 7-значное число, цифры имеют разницу 1.000e-06.
Таким образом, для каждого шага десятичного числа текста, есть около 10,2 float
,
Нет проблем с кодированием этих 7 цифр.
В этом диапазоне также нет проблем с кодированием 8 цифр.
Пример: между 223 и 224 или 8 388 608,0 и 16 777 216,0.
Разница между float
значения 223/ 223 или 1.0.
Числа около нижнего конца написаны в текстовой форме «8or9.dddddd * 106«, 7 значных цифр, имеют разницу 1,0.
Нет проблем с кодированием этих 7 цифр.
Пример: между 233 и 234 или 8 589 934 592,0 и 17 179 869 184,0,
Разница между float
значения 233/ 223 или 1024,0.
Числа около нижнего конца написаны в текстовой форме «8or9.dddddd * 109«, 7 значных цифр, имеют разность 1,000.0.
Теперь у нас есть проблема. Начиная с 8 589 934 592,0, то следующие 1024 числа в текстовой форме имеют только 1000 различных float
кодирование.
7 цифр в форме d.dddddd * 10Экспо слишком много комбинаций для уникального кодирования с использованием float
,
Иногда достаточно легко найти контрпримеры.
#include <stdio.h>
#include <string.h>
int main(void) {
int p6 = 1e6;
int p7 = 1e7;
for (int expo = 0; expo < 29; expo++) {
for (int frac = p6; frac < p7; frac++) {
char s[30];
sprintf(s, "%d.%06de%+03d", frac / p6, frac % p6, expo);
float f = atof(s);
char t[30];
sprintf(t, "%.6e", f);
if (strcmp(s, t)) {
printf("<%s> %.10e <%s>\n", s, f, t);
break;
}
}
}
puts("Done");
}
Выход
<8.589973e+09> 8.5899735040e+09 <8.589974e+09>
<8.796103e+12> 8.7961035080e+12 <8.796104e+12>
<9.007203e+15> 9.0072024760e+15 <9.007202e+15>
<9.223377e+18> 9.2233775344e+18 <9.223378e+18>
<9.444738e+21> 9.4447374693e+21 <9.444737e+21>
<9.671414e+24> 9.6714134744e+24 <9.671413e+24>
<9.903522e+27> 9.9035214949e+27 <9.903521e+27>
<1.000000e+28> 9.9999994421e+27 <9.999999e+27> This is an interesting one