Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
л_4_Операции_и_выражения.doc
Скачиваний:
11
Добавлен:
05.11.2018
Размер:
660.99 Кб
Скачать

Потеря (исчезновение) порядка

Пусть для представления вещественного числа выделено 3 разряда под мантиссу и 2 под порядок.

Тогда при делении двух чисел 0.425*10-27 и 0.561*1078 имеем:

0.425*10-27/0.561*1078 = 0.0757575…*10-105 = 0.757575…*10-106 = 0, т.е. все значения с порядком, меньшим чем (-99) будут восприниматься как «машинный нуль».

Катастрофическая потеря порядка

Пусть для представления вещественного числа выделено 3 разряда под мантиссу и 2 под порядок. Тогда имеем:

0.425*10-27/0.561*1078*0.200*1087=0.0757575…*10-105*0.200*1087=0.757575…* 10-106*0.200*1087=0, т.к. из предыдущего примера мы знаем, что 0.757575…*10-106 есть 0 (вместо ожидаемого результата 0.1515151*10-19).

Потеря значащих цифр

При выполнении операций сложения и вычитания вещественных чисел может произойти потеря значащих цифр. Например,

  • 10-9.82 = 0.18 (ожидаем)

10-9.82 = 0.100*1002–0.982*1001 = 0.100*1002–0.0982*1002= 0.100*1002–0.098*1002 = 0.002*1002 = 0.2 (получаем, если проводилось усечение).

  • 1000 – 999 = 1 (ожидаем)

1000 – 999 = 0.100*1004 – 0.999*1003 = 0.100*1004 – 0.0999*1004 = 0.100*1004 – 0.100*1004=0 (получаем, если проводилось округление) или 1000 – 999 = 0.100*1004 – 0.999*1003 = 0.100*1004 – 0.0999*1004 = 0.100*1004 – 0.099*1004=0.001*1004 =10 (вместо ожидаемой 1, если проводилось усечение).

Особенности выполнения операций отношения (сравнения)

Два вещественных числа редко бывают точно равны. При работе с вещественными числами следует избегать проверки их на равенство. Необходимо не напрямую сравнивать числа, а произвести вычитание их по модулю и сравнивать эту разность с некоторым, наперед заданным, малым числом.

Это связано с погрешностью представления вещественных значений в памяти. Значение величины, с которой сравнивается модуль разности, следует выбирать в зависимости от решаемой задачи и точности переменных, участвующих в выражении. Снизу эта величина ограничена константами, определенными в заголовочном файле <float.h>:

FLT_EPSILON = 1.192092896e-07F и

DBL_EPSILON = 2.2204460492503131e-016.

Примеры сравнения вещественных чисел на точное равенство:

int main()

{float x=1;

x+=0.5; // значение x 1.5 имеет тип float

bool b=(x==1.5); //константа 1.5 имеет тип double

// значение x и константа 1.5 представляются в 2 с/с точно, поэтому

cout << b << endl; // 1 (true) !!!

_getch();

return 0;

}

int main()

int main()

{float x=1;

x+=0.1; // значение x 1.1 имеет тип float

bool b=(x==1.1); //константа 1.1 имеет тип double

// значение x и константа 1.1 представляются в 2 с/с бесконечным числом, поэтому

cout << b << endl; // 0 (false) !!!

_getch();

return 0;

}

НО!!!

int main()

int main()

{double x=1;

x+=0.1; // значение x 1.1 имеет тип double

bool b=(x==1.1); //константа 1.1 имеет тип double

// хотя значение x и константа 1.1 представляются в 2 с/с бесконечным числом

cout << b << endl; // 1 (true) !!!

_getch();

return 0;

}

Обратить внимание

Названные особые случаи нарушают иногда и привычную логику организации вычислений. Рассмотрим два возможных случая.

Пример 1. Вычисление y(x) = (1-cos x) / x2. Эта формула не работает для малых значений х (уменьшаемое равно 1, вычитаемое стремится к 1, все делится на малое число). Для получения правильных результатов формулу необходимо преобразовать к виду z(x) = 2 sin2 (x/2) / x 2.

Пример 2. Не всегда выполняется и ассоциативный закон сложения. Добавление или вычитание малого (даже относительно малого) числа может никак не сказаться на результате (например, 1Е+15 и 1Е+10); необходимо перестроить формулу вычислений настолько, насколько это позволяет алгоритм или вводить общий масштаб. Проанализируйте примеры:

int main()

{ double x, y, z, r, v;

x=1.1e22; y = -1.1e22; z= 1.0e2;

r= (x+y)+z; cout<<r<<endl; //напечатает 100, т.к.

//(1.1e22 + -1.1e22) + 1.0e2 = 0.0e22 + 1.0e2 = 100

r= x+(y+z); cout << r <<endl; //напечатает 0, т.к.

//1.1e22 + (-1.1e22 + 1.0e2) = 1.1e22 + (-1.1e22) = 0

_getch();

return 0;

}

Пример 3. Обращайте внимание на типы переменных и констант. Их несовпадение может доставить массу неприятностей. Проанализируйте примеры:

int main()

{double a,b;

b=2.0e4 + 1.0; //тип констант double и совпадает с типом переменной

a=b-2.0e4;

printf ("%lf\n", a); //1.000000

b=2.0e20 + 1.0;

a=b-2.0e20;

printf ("%lf\n", a); //0.000000

_getch();

return 0;

}

НО!!!

int main()

{ float a,b;

b=2.0e4 + 1.0; //тип констант double и не совпадает с типом переменной

a=b-2.0e4;

printf ("%f\n", a); //1.000000

b=2.0e20 + 1.0;

a=b-2.0e20;

printf ("%f\n", a); //4008175468544.000000

//из-за различной точности представления значений типов float и double

_getch();

return 0;

}