- •Лабораторная работа №1 Линейные и разветвленные программы
- •Примеры использования арифметических операций
- •Примеры использования операций инкремента и декремента
- •Примеры использования операций отношения
- •Примеры использования поразрядных логических операций: Поразрядное логическое умножение:
- •Логический сдвиг:
- •Поразрядное логическое сложение:
- •Сложение по модулю 2 (исключающее или):
- •Примеры более сложного использования поразрядных логических операций:
- •Примеры записи логических выражений:
- •Алгебраические выражения
- •Пример вычисления значения выражения
- •Примеры использования преобразования типов
- •2. Особенности преобразования типов int и double:
- •6. Приведение типа в случае использования указателей:
- •7. Преобразование старшего типа к младшему удобно использовать для выделения отдельных частей переменной или константы:
- •9. Проанализируйте преобразование типов в операторах присваивания:
- •Особенности представления чисел по двоичному основанию
- •Особенности выполнения арифметических операций над вещественными числами
- •Операции умножения и деления
- •Ошибки округления
- •Переполнение (ошибка переполнения)
- •Потеря порядка (ошибка исчезновения порядка)
- •Катастрофическая потеря порядка (ошибка потери порядка)
- •Операции сложения и вычитания
- •Переполнение (ошибка переполнения)
- •Потеря значащих цифр
- •Особенности выполнения операций отношения (сравнения)
- •Правила работы с данными вещественных типов
- •Замечания по программированию алгебраических выражений
- •Условный оператор
- •Оператор switch Пример 1. Печать событий в зависимости от года
- •Пример 2. Распознавание вводимых символов
- •Пример 3. Печать сообщений о работе программы
- •Пример 4. Селектор перечислимого типа
Катастрофическая потеря порядка (ошибка потери порядка)
Ситуация появления «машинного нуля» в ряде случаев приводит к так называемой катастрофической потере порядка. Например, пусть для представления вещественного числа выделено 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.
Указанные исключительные ситуации нужно отслеживать и специально обрабатывать в программе.
Операции сложения и вычитания
При сложении и вычитании вещественных чисел процессор выполняет следующие действия:
-
выравнивание мантисс, т.е. сдвиг мантиссы числа с меньшим порядком на число разрядов, равное разности порядков
-
вычисляет сумму (разность) мантисс операндов
-
определяет порядок результата как больший из порядков операндов
-
нормализует мантиссу результата, если это необходимо
-
выполняет соответствующую корректировку порядка результата, если это необходимо.
При этом возможно возникновение некоторых особых (исключительных) ситуаций, как последствий действий процессора.
Переполнение (ошибка переполнения)
Из-за ограниченности разрядов под представление мантиссы и порядка числа при выполнении операций сложения и вычитания возможен «выход» значения мантиссы или порядка за разрядную сетку. Например, пусть для представления вещественного числа выделено 3 разряда под мантиссу и 2 под порядок. Тогда при сложении двух чисел 0.999*1098 и 0.976*1099 имеем:
0.999*1098 + 0.976*1099 = 0.099(9)*1099 + 0.976*1099 = 0.100*1099 + 0.976*1099 = 1.076*1099 = 0.108*10100 = 0.108*10** т.к. числа сначала приводятся к одинаковому – большему порядку, затем производится сложение мантисс и после этого повторная нормализация, при которой значение нового порядка 100 выходит за отведенные ему 2 разряда, т.е. имеем ошибку переполнения.
Потеря значащих цифр
При выполнении операций сложения и вычитания вещественных чисел может произойти потеря значащих цифр. Например,
-
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, если проводилось усечение).
Как следствие из всего сказанного: при выполнении арифметических операций сложения и вычитания над вещественными числами возможны «особые» случаи, нарушающие привычную логику организации вычислений. Рассмотрим два возможных случая.
Пример 1. Вычисление y(x) = (1-cos x) / x2. Эта формула не работает для малых значений х (уменьшаемое равно 1, вычитаемое стремится к 1, все делится на малое число). Для получения правильных результатов формулу необходимо преобразовать к виду z(x) = 2 sin2 (x/2) / x 2.
Пример 2. Не всегда выполняется и ассоциативный закон сложения. При сложении одинаковых чисел с разными знаками происходит потеря значимости. Проанализируйте примеры:
float 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 v= x+y; r= v+z; cout << r <<endl; //напечатает 100, т.к. //(1.1e22 + -1.1e22 )+ 1.0e2 = 0.0e22 + 1.0e2 = 100 v= y+z; r= x+v; cout << r << endl; //напечатает 0, т.к. //1.1e22 + (-1.1e22 + 1.0e2) = 1.1e22 + (-1.1e22) = 0