1 Востаннє редагувалося drWoZD (07.03.2014 10:57:34)

Тема: Дробові числа. Частина 2

Як і обіцяв в першій статті, в цій я напишу про деякі проблеми такого представлення чисел.
Перша і напевно найбільш очевидна проблема: існує певна похибка при збережені чисел, при чому чим дальше від 0 число тим більша похибка. Це можна перевірити на такому прикладі:

Прихований текст
#include<stdio.h>

main()
{
   float a = 999999999,
         b = 999999909;
    
    printf("%f\n", a-b);
}

З математичної точки зору результат мав би бути 90.0, але насправді буде виведено 64.0.
Все тому, що 999999999d = 4e6e6b28h(ieee) = 1000000000d, тобто похибка склала +1.
А 999999909d = 4e6e6b27h(ieee) = 999999936d, а тут +27.

Другою проблемою є помилки при приведені типів. Для прикладу:

Прихований текст
#include<stdio.h>

main()
{
    double a = 123456789.123456;
    float  b = 123456789.123456;
      
    printf("%f\n", a-b);
}

Знов ж таки, з математичного боку мало б вивести 0.0 але насправді виводить -2.876544. Загалом причина цього майже така ж як і попередня. Число в типі double, було збережено більш точно ніж в типі float через що вийшов такий результат.
Щоб цього уникнути треба перед  такими діями приводити числа до одного типу, а якщо конкретніше до до того типу який має меншу розрядність. Тобто останню стрічку цього прикладу треба замінити на

printf("%f\n", (float)a-b);

Для цієї проблеми можна навести ще один приклад

Прихований текст
#include<stdio.h>

main()
{
    float a = 1,
          b = 3,
          c;
     
    c = a/b - 1/3;
    
      
    printf("%f\n", c);
}

І знову мало б вивести 0, але виводить 0.333333. Це відбувається тому що вираз 1/3 обчислюється з більшою точністю а саме в типі double. Щоб цього уникнути треба явно привести числа 1 і 3 до типу float, або зберігати змінні a, b, c в типі double.

Наступна проблема в неправильності виконання операцій з дуже різними числами, тобто тими в яких не перетинаються експоненти. Приклад:

Прихований текст
#include<stdio.h>

main()
{
    float a = 100.0,
          b = 0.00001;
     
    for (int i = 0; i < 10000000; i++)
         a -=b;
    
      
    printf("%f\n", a);
}

Ще раз мало б вивести 0 але виводить 8.299232. Тут просто треба робити компенсацію:

Прихований текст
#include<stdio.h>

main()
{
    float a  = 100.0,
          b  = 0.00001,
          c  = 0,
          a1 = 0,
          b1 = 0;
     
    for (int i = 0; i < 10000000; i++)
    {
        b1 = b - c;
        a1 = a - b1;
        c = (a - a1) - b1;
        a = a - b1;
    }
    
      
    printf("%f\n", a);
}

Хоча невелика похибка залишилась( а саме 0.000003 ) ;).

P.S. Вибачайте за не надто інформативні назви змінних просто було лінь вигадувати назви.