Как компилятор отображает * неточное * значение float? - PullRequest
2 голосов
/ 10 января 2012

Я хотел бы знать механизм, почему компилятор показывает неточное значение с плавающей точкой .Пример

float a = 0.056;
printf("value = %f",a); // this prints "value = 0.056"

Если вы попытаетесь сохранить 0,056 в двоичном формате с плавающей запятой, вы получите это ( используйте эту ссылку для преобразования )

0,00001110010101100000010000011000, чторавно 0,0559999998658895

1.Как компилятор показывает 0.056, в то время как он должен показывать 0.055999999?

Давайте рассмотрим этот пример чуть дальше

#include <stdio.h>
main()
{
float a, b;

a = 0.056;
b = 0.064; // difference is 0.08 

printf("a=%f, b=%f",a,b);

if( b - a == 0.08) // this fails
    printf("\n %f - %f == %f subtraction is correct",b,a,b-a); 
else
    printf("\n%f - %f != %f Subtraction has round-off error\n",b,a,b-a);
}

Обратите внимание, что здесь выполняется блок else, в то время как мы ожидаем, что блок будетправильный.Вот выходные данные.

a=0.056000, b=0.064000
0.064000 - 0.056000 != 0.008000 Subtraction has round-off error

Снова значения отображаются так, как мы ожидаем (без ошибки округления), но эти значения имеют ошибки округления, но отображаются ложные замаскированные значения.Мой второй вопрос

2.Есть ли способ показать действительное значение сохраненного числа, а не замаскированное, которое мы ввели?

Примечание. Я включил код C в Visual Studio 2008, но он должен быть воспроизводимымна любом языке.

Ответы [ 5 ]

11 голосов
/ 10 января 2012

Компилятор ничего не показывает ? Ваша программа показывает 0.056, потому что %f показывает результат только до 6 цифр.Попробуйте %.16f, если хотите увидеть все неточности (Результат: http://ideone.com/orrkk).

Страница руководства printf показывает множество других опций, которые вы можете использовать с этими спецификаторами.

4 голосов
/ 10 января 2012

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

2 голосов
/ 10 января 2012

Я вижу много разговоров о printf и о том, как он печатает материал «не в ту сторону», потому что все округляется и т. Д. printf печатает именно то, что вы ожидаете , когда выобратите внимание, что фактическое число, хранящееся в a , равно 0.05600000172853469848.

OP предполагает, что число, сохраненное в нем, равно 0.0559999..., но при взгляде на фактическое число видно, чтонеправильно:

#include <stdio.h>

int main() {
        float a = 0.056;
        printf("%A\n", a);
}

Это напечатает 0X1.CAC084P-5, что означает, что наша мантисса (0xCAC084) равна 110010101100000010000100.Это 24 бита, но не 23, которые мы можем хранить в 32-битной (IEEE-754 одинарной точности) плавающей запятой, что означает, что то, что там есть, на самом деле 11001010110000001000010

Помните, что мантисса нормализована и предполагаетсяначнем с 1, поэтому, применяя показатель степени и т. д., наш номер:

0.0000111001010110000001000010

, что переводится как 0.05600000172853469848

. ОП предполагает, что вместо этого:

0.00001110010101100000010000011

, что, безусловно, является более точным, НО, который требует немного больше, чем может хранить мантисса, так что мы бы закончили с этим:

0.0000111001010110000001000001

или 0.05599999800324440002.

Конечно, ни одно из чисел не является 0.056, но ошибка в представлении выше для последнего!Поэтому неудивительно, что мы получаем то, что получаем ...

1 голос
/ 10 января 2012

Вы делаете ошибку, полагая, что ваш флот был когда-либо точным.

Они не предназначены для представления точного значения, такого как 0.0559999998658895. Для этого существуют такие библиотеки, как GMP .

Поплавки спроектированы так, чтобы быть быстрыми и приблизительными.

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

printf не знает, что вы считаете 0.056 «правильным». Он просто знает, что число с плавающей точкой, напечатанное в удобочитаемом формате, имеет точность до 6 значащих цифр, а 0.0560000 представляет наиболее близкое совпадение с использованием такого количества цифр.

1 голос
/ 10 января 2012

2) Если у вас есть библиотека C99, попробуйте распечатать двойной в шестнадцатеричном

printf("%A\n", 56.0/100);
...