- •Лекция 4 Операции и выражения
- •Понятие выражения и операции
- •Классификация операций
- •Приоритеты (ранги) операций
- •Арифметические операции
- •Примеры использования арифметических операций Выделение цифр в целом числе
- •Выделение цифр в вещественном числе
- •Арифметические операции над переменными символьного типа
- •Операции инкремента и декремента
- •Особенности выполнения операций инкремента и декремента
- •Операции присваивания
- •Примеры использования
- •Операции отношения
- •Логические операции
- •Особенности выполнения логических операций
- •Примеры использования логических операций
- •Логические выражения и примеры их записи
- •Поразрядные логические операции
- •Примеры выполнения поразрядных логических операций:
- •Операции поразрядного сдвига
- •Дополнительные операции Операции доступа к компонентам структурированного объекта
- •Операции доступа к адресуемым компонентам классов
- •Определение размера sizeof
- •Вызов функции ( )
- •Индексация [ ]
- •Операция запятая ,
- •Операция расширения области видимости ::
- •Преобразование типа
- •Правила преобразования типов
- •Примеры преобразования типов
- •2. Особенности преобразования типов int и double:
- •7. Проанализируйте преобразование типов в операторах присваивания:
- •8. Приведение типа в случае использования указателей:
- •9. Преобразование старшего типа к младшему удобно использовать для выделения отдельных частей переменной или константы:
- •10. С помощью приведения типа можно получить дробную часть числа:
- •Программирование вычисления алгебраических выражений
- •Определение первого символа вводимого значения
- •Контроль правильности ввода значения переменной
- •Основные встроенные (стандартные) функции
- •Пример вычисления по формуле
- •Замечания по программированию алгебраических выражений
- •Особенности представления чисел по двоичному основанию*
- •Особенности выполнения арифметических операций над вещественными числами*
- •Операции умножения и деления
- •Операции сложения и вычитания
- •Особые ситуации Ошибки округления
- •Ошибки переполнения
- •Потеря (исчезновение) порядка
- •Катастрофическая потеря порядка
- •Потеря значащих цифр
- •Особенности выполнения операций отношения (сравнения)
- •Обратить внимание
- •Правила работы с данными вещественных типов
Потеря (исчезновение) порядка
Пусть для представления вещественного числа выделено 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;
}