ЛР-2 / ЯП №2
.odtМинистерство науки и высшего образования Российской Федерации
Федеральное государственное автономное образовательное учреждение
высшего образования
ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР)
Кафедра безопасности информационных систем (БИС)
ВЕЩЕСТВЕННЫЕ ЧИСЛА. ОШИБКИ ПРИ РАБОТЕ С ВЕЩЕСТВЕННЫМИ ЧИСЛАМИ
Отчет по лабораторной работе №2
по дисциплине «Языки программирования»
Студент гр.
_______
_______
Принял:
Доцент каф. КИБЭВС, д.т.н., доцент
_______ Романов А. С.
_______
Оглавление
Введение 3
1 Ход работы 4
Заключение 13
Приложение А 14
Введение
Целью работы является знакомство с основными ошибками, возникающими при обработке вещественных чисел.
Для выполнения работы был выбран ЯП Python.
Задание:
1. Изучить теоретические сведения.
2. В качестве исходных значений принять следующие значения:
x = <номер студенческого билета><номер группы>,<дата рождения ддммгггг>;
y = x * 10-10;
c = x + y;
c’ = округление с до 9 знака после запятой;
z = x ± 10-8 ;
k = округлить до целого(х).
Произвести вычисления, и показать каким образом возникают ошибки при работе с вещественными числами. Все значения вычислять с максимальной точностью, не округляя. Относительную ошибку вычислять с точность до 10 значащих цифр.
3. Объяснить полученные результаты.
4. Для выбранного варианта задания изучить, какие типы данных есть в языке программирования, как они реализованы.
5. Написать программы, демонстрирующие ошибки вещественных чисел.
6. Написать отчет и защитить у преподавателя.
1 Ход работы
Ниже приведены точные расчеты чисел:
x = 24730187341,24022006;
y = x*10-10 = 2,473018734124022006;
c = x+y = 24730187343,713238794124022006;
c’ = 24730187343,713238794;
z1 = x+10-8 = 24730187341,24022007;
z2 = x-10-8 = 24730187341,24022005;
k = 24730187341.
Существуют три основные ошибки вычислений, возникающие при выполнении операции над вещественными числами:
Исчезновение операнда – операнд может исчезнуть, если он относительно мал по отношению с другим операндом. В данной работе можно увидеть данную ошибку при вычислении чисел z1 и z2.
Умножение ошибки – многократное увеличение абсолютной погрешности операнда, которая может появиться при использовании арифметики с плавающей точкой, даже если относительная ошибка мала. Зачастую это является результатом умножения или деления. Данная ошибка наблюдается при вычислении y.
Потеря значимости – полная потеря значимости, вызванная вычитанием почти равных чисел.
В ходе работы было запрограммировано значения из задания (рисунок 1.1), а также получены результаты вычислений (рисунок 1.2).
Рисунок 1.1 — Запрограммированная последовательность вычислений
Рисунок 1.2 — Результаты вычислений на ЯП Python
При вычислении числа «x», с помощью программы мы получили x = 24730187341,24021912. Следовательно, можно сделать вывод, что абсолютная ошибка при расчете равна 0,00000094, а относительная ошибка равна 0,000000000000000380102.
При вычислении числа «y» получили y = 2,473018734124022178, из чего можно сделать вывод, что абсолютная ошибка равна 0,000000000000000172, а относительная – 0,0000000000000000695508.
Во время расчета числа «c» получили c = 24730187343,713237762451171875. В данной ситуации абсолютная ошибка равна 0,000001031672850131, а относительная 0,00000000000000004172. При округлении числа c до 9 знаков после запятой получилось c’ = 24730187343,713237762. Это означает, что абсолютная ошибка равна 0,000001032, а относительная – 0,0000000000000000417298.
Числа «z1» и «z2» получились равными 24730187341.240219116210938. Следовательно, абсолютная ошибка равна 0.000000953789062 и 0,000000933789062 соответственно, а относительная ошибка равна 0.000000000000000038565 и 0.000000000000000037755 соответственно.
При округлении числа до целого ошибки отсутствуют.
Также по заданию были разобраны примеры и приведены коды их решения на ЯП Python (рисунок 1.3-1.14)
Рисунок
1.3 — Код для ошибки Исчезновение операнда
Рисунок
1.4 — Вывод для кода ошибки 1
Видно, что значения совпадают, следовательно сложение и вычитание 10-8 не дает ничего, операнд пропадает.
Рисунок
1.5 — Код для ошибки Умножение ошибки
Рисунок
1.6 — Вывод для кода ошибки 2
Видно, что при изменении числа на очень малую величину, их квадраты отличаются.
Рисунок
1.7 — Код для ошибки Потеря значимости
Рисунок
1.8 — Вывод для кода ошибки 3
При изменении числа на одну величину, а затем вычитания исходного числа, получается так, что число на которое увеличили изменяется.
Р
исунок
1.9 — Код для Программный вывод битов
(IEEE 754)
Р
исунок
1.10 — Вывод для Программный вывод
битов (IEEE 754)
Судя по битам, даже малейшее изменение исходного числа, приводит к огромным различиям в битах.
Р
исунок
1.11 — Код для Определение машинного
эпсилона
Рисунок
1.12 — Вывод
для Определение машинного эпсилона
Как видно из вывода, было реализовано определение машинного эпсилона и судя по тому что значение совпало с ulp из math, реализовано верно.
Рисунок
1.13 — Код для Метода Кахана
Рисунок 1.14 —
Вывод
для Метода Кахана
Как можно видеть, точная сумма находится верно, тогда как обычная имеет ошибку.
Всего в языке программирования Python есть 8 типов данных:
int – целые числа;
float – числа с плавающей точкой;
complex – комплексные числа (числа вида a + b*i, где a и b – вещественные числа, а i – мнимая единица, т.е. число для которого выполняется i^2 = -1);
string – строки;
bool – логический тип;
list – списки;
tuple – кортежи;
dict – словари;
set – множества.
В данной работе использовался тип данных float, который отвечает за числа с плавающей точкой. Переменная такого типа может принимать значения от 2.2^-308 до 1.7^308. Переменные типа int как и в других языках могут принимать значения от -2 147 483 648 до 2 147 483 648.
Заключение
В ходе выполнения лабораторной работы было произведено знакомство с основными ошибками, возникающими при обработке вещественных чисел, а также работа с ними на практике, рассчитаны относительные ошибки. А также составлены коды для примеров из методического материала и даны комментарии к ним.
Приложение А
(обязательное)
Листинг кода для лабораторной работы
from math import *
x = float(24730187341.24022006)
y = x * float(10**-10)
c = x + y
z1 = x + float(10**-8)
z2 = x - float(10**-8)
k = floor(x)
print(f'x = {x:.8f}')
print(f'y = x*10^-10 = {y:.18f}')
print(f'c = x+y = {c:.18f}')
print(f'c1 = округлить c до 9 знаков = {round(c,9):.9f}')
print(f'z+ = x+10^-8 = {z1:.15f}')
print(f'z- = x-10^-8 = {z2:.15f}')
print(f'k = floor(x) = {k}')
print('\n' + ' '*60)
print('ОШИБКА 1: ИСЧЕЗНОВЕНИЕ ОПЕРАНДА')
print(' '*60)
print(f'x = {x:.8f}')
print(f'z+ = {z1:.8f}')
print(f"z- = {z2:.8f}")
print('\n' + ' '*60)
print('ОШИБКА 2: УМНОЖЕНИЕ ОШИБКИ')
print(' '*60)
m = y*(10**3)
n = y*(10**3)+0.000001
z = m*m
v = n*n
print(f'm = {m:.9f}')
print(f'n = {n:.9f}')
print(f'z = m*m = {z:.9e}')
print(f'v = n*n = {v:.9e}')
print('\n' + ' '*60)
print('ОШИБКА 3: ПОТЕРЯ ЗНАЧИМОСТИ')
print(' '*60)
g = c
h = x
print(f'g = {g:.8f}')
print(f'h = {h:.8f}')
print(f'g - h = {g - h:.8f}')
print(f'y = {y:.8f}')
print("\n" + " " * 60)
print("Представление чисел в памяти (IEEE 754)")
import struct
def float_to_bits(f):
"""Преобразование float в битовое представление"""
s = struct.pack('>d', f)
i = struct.unpack('>q', s)[0]
return bin(i)[2:].zfill(64)
def print_float_bits(value, name):
bits = float_to_bits(value)
sign = bits[0]
exponent = bits[1:12]
mantissa = bits[12:]
print(f"\n{name} = {value}")
print(f" Биты: {bits}")
print(f" Знак: {sign} | Экспонента: {exponent} | Мантисса: {mantissa[:13]}...")
x = 12345.678
small = 0.0000001
print_float_bits(x, "x")
print_float_bits(x + small, "x + small")
print_float_bits(x, "x (снова)")
print(f"\nБиты x и x+small одинаковы? {float_to_bits(x) == float_to_bits(x + small)}")
print('\n' + ' '*60)
print('МАШИННЫЙ ЭПСИЛОН')
# float (Python float = double precision)
epsilon_float = 1.0
while 1.0 + epsilon_float / 2.0 > 1.0:
epsilon_float /= 2.0
print(f"Машинный эпсилон (float64): {epsilon_float}")
print(f"Теоретическое значение: {2**-52:.16e}")
# Проверка с помощью math.ulp
print(f"math.ulp(1.0) = {ulp(1.0)}")
print(f"Разница между двумя числами float64: {ulp(1.0)-epsilon_float}")
print('\n' + ' '*60)
print("Метод Кахана для точного суммирования")
def kahan_sum(numbers):
s = 0.0
c = 0.0 # Поправка
for num in numbers:
y = num - c
t = s + y
c = (t - s) - y
s = t
return s
# Создаем массив чисел
n = 1000000
numbers = [0.1234567] * n
# Обычная сумма
simple_sum = 0.0
for num in numbers:
simple_sum += num
# Метод Кахана
kahan = kahan_sum(numbers)
exact_sum = 0.1234567 * n
print(f"Точная сумма: {exact_sum}")
print(f"Обычная сумма: {simple_sum}")
print(f"Метод Кахана: {kahan}")
print(f"Ошибка обычной суммы: {abs(exact_sum - simple_sum)}")
print(f"Ошибка метода Кахана: {abs(exact_sum - kahan)}")
