2014-12-23 2 views
0

сегодня случилось со мной странно, когда я пытаюсь скомпилировать и выполнить вывод этого кода, это не то, что я ожидал. Вот код, который просто добавляет плавающие значения в массив float и затем распечатывает его. Простой код:Поплавок странная ошибка неточности в c

int main(){ 
    float r[10]; 
    int z; 
    int i=34; 
    for(z=0;z<10;z++){ 
     i=z*z*z; 
     r[z]=i; 
     r[z]=r[z]+0.634; 
     printf("%f\n",r[z]); 
    } 
} 

выход:

0.634000 
1.634000 
8.634000 
27.634001 
64.634003 
125.634003 
216.634003 
343.634003 
512.633972 
729.633972 

отмечают, что из 27 цифр появляется после того, .634, которые не должны быть там. Кто-нибудь знает, почему это произошло? Это событие, вызванное точечном приближении плавающим ..

PS У меня есть система Linux Debian, 64 бит

благодарит всех

+2

Поплавкая неточность не является неожиданностью - http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – Luizgrs

+0

Yous 'double' вместо этого, и это должно быть лучше, но вы никогда не сможете избежать это с плавающей запятой. –

+0

прочитайте [this] (http://floating-point-gui.de/) сначала, затем прочитайте статью оракула выше –

ответ

5

ряд может быть представлен в следующем виде:?

[знак] [мантисса] * 2 [показатель]

Так что будет roundi ng или относительные ошибки, когда пространство меньше в памяти.

От wiki:

одинарной точности в формате с плавающей точкой является форматом номер компьютера, который занимает 4 байта (32 бита) в памяти компьютера и представляет широкий динамический диапазон значений с помощью плавающей точка.

enter image description here

Стандарт IEEE 754 определяет binary32 как имеющий:

Sign bit: 1 bit 
Exponent width: 8 bits 
Significand precision: 24 bits (23 explicitly stored) 

Это дает от 6 до 9 значащих десятичных цифр точности (если десятичной строки с по наиболее 6 значащих десятичных знаков преобразуются в IEEE 754 одиночной точности, а затем преобразуются обратно к тому же числу Значительное десятичное число, тогда финальная строка должна соответствовать оригиналу; , и если одиночная точность IEEE 754 преобразуется в десятичную строку с не менее чем 9 значащими десятичными знаками, а затем преобразуется обратно в один, , то окончательное число должно соответствовать оригиналу [4]).

Edit (комментарий Эдварда): Больших (больше бит) с плавающей точкой представление позволяет более точно.

+1

* «чем больше число с плавающей запятой, тем хуже точность» * является неточным заявлением. Я думаю, вы имеете в виду, что более крупные (более бит) представления с плавающей запятой допускают большую точность. – Edward

+0

@Edward - да, это может ввести в заблуждение - отредактировано спасибо. – Sadique

0

«float» имеет точность только около шести цифр, поэтому не обязательно, чтобы вы получили большие ошибки.

Если вы использовали «double», у вас было бы около 15 цифр точности. У вас есть есть ошибка, но вы бы получили, например, 125.634000000000003, а не 125.634003.

Таким образом, вы всегда будете получать ошибки округления, и ваши результаты не будут довольно, что вы ожидаете, но при использовании двойного эффекта будет минимальным. Предупреждение: если вы делаете что-то вроде добавления 125 + 0.634, а затем вычитаете 125, результат будет (скорее всего) не be 0.634. Независимо от того, используете ли вы float или double. Но с двойным результатом будет очень, очень близко к 0.634.

В принципе, учитывая выбор float и double, вы никогда не должны использовать float, если у вас нет очень-очень веской причины.

1

Да, это погрешность приближения floating point или Round-off error. Представление чисел с плавающей запятой использует quantization для представления большого диапазона чисел, поэтому он представляет только шаги и округляет все промежуточные числа до ближайшего шага. Это вызывает ошибку, если требуемый номер не является одним из этих шагов.

1

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

int main(){ 
    float r[10]; 
    int z; 
    int i=34; 
    for(z=0;z<10;z++){ 
     i=z*z*z; 
     r[z]=i; 
     r[z]=r[z]+0.634; 
     printf("%.30f\n",r[z]); 
    } 
} 

дает

0.634000003337860107421875000000 
1.633999943733215332031250000000 
8.633999824523925781250000000000 
27.634000778198242187500000000000 
64.634002685546875000000000000000 
125.634002685546875000000000000000 
216.634002685546875000000000000000 
343.634002685546875000000000000000 
512.633972167968750000000000000000 
729.633972167968750000000000000000 

В частности, обратите внимание, что 0.634 не на самом деле " 0,634 ", но вместо этого это самое близкое число, представляемое float.