2015-03-31 3 views
-1
#include <stdio.h> 
#include <float.h> 
int main(int argc, char** argv) 
{ 
    long double pival = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899L; 
    float pival_float = pival; 
    printf("%1.80f\n", pival_float); 
    return 0; 
} 

Выход я на GCC является:Почему плавать точнее, чем должно быть?

3.14159274101257324218750000000000000000000000000000000000000000000000000000000000 

Поплавок использует 23 бита mantisa. Таким образом, максимальная доля, которая может быть представлена, равна 2^23 = 8388608 = 7 десятичных цифр точности.

Но вышеприведенный результат показывает 23 десятичных цифры точности (3.14159274101257324218750). Я ожидал, что он напечатает 3.1415927 000000000000 ....)

Что я пропустил, чтобы понять?

+1

поплавки являются * бинарными *. Они не имеют * «десятичных цифр точности» точно. – hyde

+1

Прочитайте http://floating-point-gui.de/ –

+0

Фактически float имеет 24 бита значительного (23 бит мантиссы + 1 скрытый бит) –

ответ

10

У вас есть только 7 цифр точности. Пи

3,141592 6535897932384626433832795028841971693993751058209 ...

Но вывод, который вы получили от печати вашего приближения поплавка к Pi был

3,141592 74101257324218750000 ...

Как вы можете видеть значения отклоняются от 7-й цифры после десятичной точки.

Если вы запросите printf() на 80 знаков после десятичного знака, он распечатает много цифр десятичного представления двоичного значения, хранящегося в поплавке, даже если это число намного больше, чем точность, разрешенная плавающее представление.

4

Бинарное значение с плавающей запятой не может точно представлять 3.1415927 (так как это не точная двоичная дробь). Ближайшее значение, которое оно может представляет 3.1415927410125732421875, так что это фактическое значение вашего pival_float. Когда вы печатаете pival_float с восемью цифрами, вы видите его точное значение, плюс кучу нулей для хорошей оценки.

3

Ближайший float значение пи имеет двоичное кодирование ...

0 10000000 10010010000111111011011 

... в котором я вставленный пробелы между знаком, показателем и мантиссы. Показатель смещен, поэтому биты выше кодируют множитель 2^1 == 2, а мантисса кодирует долю выше 1, причем первый бит стоит половину, и каждый бит после этого стоит вдвое меньше, чем бит до.

Поэтому мантиссы биты выше стоит:

1 x 0.5 
0 x 0.25 
0 x 0.125 
1 x 0.0625 
0 x 0.03125 
0 x 0.015625 
1 x 0.0078125 
0 x 0.00390625 
0 x 0.001953125 
0 x 0.0009765625 
0 x 0.00048828125 
1 x 0.000244140625 
1 x 0.0001220703125 
1 x 0.00006103515625 
1 x 0.000030517578125 
1 x 0.0000152587890625 
1 x 0.00000762939453125 
0 x 0.000003814697265625 
1 x 0.0000019073486328125 
1 x 0.00000095367431640625 
0 x 0.000000476837158203125 
1 x 0.0000002384185791015625 
1 x 0.00000011920928955078125 

Таким образом, наименьший значащий бит после умножения показателя закодированные значения "2" стоит ...

0.000 000 238 418 579 101 562 5 

I добавлены пробелы, чтобы было легче подсчитать, что последняя цифра не 0 находится в 22nd десятичная запятая.

Значение вопрос говорит printf() отображается ниже появится рядом с вкладом наименее значащий бит мантиссы:

3.14159274101257324218750000000000000000000000000000000000000000000000000000000000 
0.0000002384185791015625 

Очевидно, что наименее значащие цифры выстраиваются правильно. Если вы добавили все вклады мантиссы выше, добавили неявный 1, а затем умножить на 2, вы получите точный значение printf. Это объясняет, как значение float равно точно (в математическом смысле нулевой случайности) значение, показанное printf, но сравнение ниже pi показывает только первые 6 знаков после запятой, точный, учитывая конкретное значение, которое мы хотим магазин.

3.14159274101257324218750000000000000000000000000000000000000000000000000000000000 
3.14159265358979323846264338327950288419716939937510582097494459230781640628620899 
     ^

В вычислительной технике, это общий для обозначения точности с плавающей точкой типа, когда мы на самом деле заинтересованы в точности мы можем положиться. Я полагаю, вы могли бы утверждать, что, будучи изолированным, точность поплавков и удвоений бесконечна, округление, необходимое при их использовании для приближения чисел, которые они не могут кодировать идеально, для большинства практических целей случайное, и в этом смысле они предлагают конечные значимые цифры точности при кодировании таких чисел.

Таким образом, printf не является неправильным отображать столько цифр; в некоторых приложениях может использоваться float для кодирования этого точного номера (почти наверняка, поскольку природа вычислений приложения включает в себя суммы значений 1/2^n), но это было бы исключение, а не правило.

2

Ведя от ответа Тони, один из способов доказать это ограничение на десятичной точность для себя на практику просто объявить pi как много десятичных точек, как вам нравится, а присваивание значения к float. Затем посмотрите, как он хранится в памяти.

То, что вы найдете, не важно, сколько знаков после запятой вы даете ему значение 32-bit в памяти всегда будет эквивалентом unsigned значения 1078530011 или 01000000010010010000111111011011 в двоичной системе. Это объясняется, как объясняют другие, Формат одноточечной плавающей точки IEEE-754 Ниже приведен простой бит кода, который позволит вам убедиться, что это ограничение означает, что это ограничение pi, как плавающее число, ограничено шестью десятичными знаками точность:

#include <stdio.h> 
#include <stdlib.h> 

#if defined (__LP64__) || defined (_LP64) 
# define BUILD_64 1 
#endif 

#ifdef BUILD_64 
# define BITS_PER_LONG 64 
#else 
# define BITS_PER_LONG 32 
#endif 

char *binpad (unsigned long n, size_t sz); 

int main (void) { 

    float fPi = 3.1415926535897932384626433; 

    printf ("\n fPi : %f, in memory : %s unsigned : %u\n\n", 
      fPi, binpad (*(unsigned*)&fPi, 32), *(unsigned*)&fPi); 

    return 0; 
} 

char *binpad (unsigned long n, size_t sz) 
{ 
    static char s[BITS_PER_LONG + 1] = {0}; 
    char *p = s + BITS_PER_LONG; 
    register size_t i; 

    for (i = 0; i < sz; i++) 
     *(--p) = (n>>i & 1) ? '1' : '0'; 

    return p; 
} 

Выхода

$ ./bin/ieee754_pi 

fPi : 3.141593, in memory : 01000000010010010000111111011011 unsigned : 1078530011 
Смежные вопросы