Загрузка данных


import random
import sys
from fractions import Fraction
from math import fsum


def exact(value: str) -> Fraction:
    # точное значение десятичной записи, например "0.1" -> 1/10
    return Fraction(value)


def error(result: float, true_value: Fraction) -> float:
    # абсолютная погрешность: |вычисленное - точное|
    return float(abs(Fraction(result) - true_value))


def naive_sum(values: list[float]) -> float:
    # обычное последовательное сложение (встроенный sum() в Python 3.12+
    # использует компенсированное суммирование и скрывает погрешность)
    total = 0.0
    for v in values:
        total += v
    return total


def kahan_sum(values: list[float]) -> float:
    total = 0.0
    compensation = 0.0
    for v in values:
        y = v - compensation
        t = total + y
        compensation = (t - total) - y
        total = t
    return total


def task1():
    # Сложение нескольких вещественных чисел
    raw = ["0.1", "0.2", "0.3"]
    print(f"Числа: {' + '.join(raw)}")
    values = [float(x) for x in raw]
    true_value = sum(exact(x) for x in raw)
    result = naive_sum(values)
    print(f"Результат float: {result!r}")
    print(f"Точное значение: {float(true_value)!r}")
    print(f"Погрешность: {error(result, true_value)}")
    print(f"Совпадает с точным: {result == float(true_value)}")


def task2():
    # Влияние порядка сложения и вычитания
    a, b, c = "1e16", "-1e16", "1"
    fa, fb, fc = float(a), float(b), float(c)
    true_value = exact(a) + exact(b) + exact(c)
    r1 = (fa + fb) + fc
    r2 = fa + (fb + fc)
    r3 = (fa + fc) + fb
    print(f"({a} + {b}) + {c} = {r1!r}")
    print(f"{a} + ({b} + {c}) = {r2!r}")
    print(f"({a} + {c}) + {b} = {r3!r}")
    print(f"Точное значение: {float(true_value)!r}")
    print(f"Все результаты равны: {r1 == r2 == r3}")


def task3():
    # Накопление ошибки при последовательных операциях
    value = input("Введите слагаемое: ").replace(",", ".")  # 0.1
    count = int(input("Введите число повторений: "))  # 100
    step = float(value)
    exact_step = exact(value)
    s = 0.0
    checkpoint = 1
    for i in range(1, count + 1):
        s += step
        if i == checkpoint or i == count:
            print(f"n={i}: сумма={s!r}, погрешность={error(s, exact_step * i)}")
            checkpoint *= 10
    print(f"Итого: {s!r}, ожидалось {float(exact_step * count)!r}")


def task4():
    # Различные группировки выражений
    a, b, c = "0.1", "0.2", "0.3"
    print(f"a = {a}, b = {b}, c = {c}")
    fa, fb, fc = float(a), float(b), float(c)
    ea, eb, ec = exact(a), exact(b), exact(c)

    print("Сложение:")
    print(f"  (a+b)+c = {(fa + fb) + fc!r}")
    print(f"  a+(b+c) = {fa + (fb + fc)!r}")
    print(f"  точно   = {float(ea + eb + ec)!r}")

    print("Умножение:")
    print(f"  (a*b)*c = {(fa * fb) * fc!r}")
    print(f"  a*(b*c) = {fa * (fb * fc)!r}")
    print(f"  точно   = {float(ea * eb * ec)!r}")

    print("Распределительный закон:")
    print(f"  (a+b)*c   = {(fa + fb) * fc!r}")
    print(f"  a*c + b*c = {fa * fc + fb * fc!r}")
    print(f"  точно     = {float((ea + eb) * ec)!r}")


def task5():
    # Влияние масштабов чисел
    small = input("Введите малое число: ").replace(",", ".")  # 1
    fs = float(small)
    es = exact(small)
    print(f"Вычисление (10^k + {small}) - 10^k, точный ответ {small}:")
    for k in range(0, 21, 2):
        big = 10.0 ** k
        r = (big + fs) - big
        print(f"  k={k:2d}: {r!r:>8}  погрешность={error(r, es)}")
    print(f"Машинный эпсилон: {sys.float_info.epsilon}")


def task6():
    # Серия умножений и делений
    value = input("Введите число: ").replace(",", ".")  # 0.1
    n = int(input("Введите n: "))  # 20
    x = float(value)
    ex = exact(value)
    r = x
    for k in range(1, n + 1):
        r *= k
    for k in range(1, n + 1):
        r /= k
    print(f"x * 1 * ... * n / 1 / ... / n = {r!r}")
    print(f"Исходное x = {x!r}")
    print(f"Абсолютная погрешность: {error(r, ex)}")
    print(f"Относительная погрешность: {error(r, ex) / abs(float(ex))}")
    print(f"(x / 3) * 3 == x: {(x / 3) * 3 == x}")
    print(f"(x * 3) / 3 == x: {(x * 3) / 3 == x}")


def task7():
    # Устойчивость при изменении порядка операций
    raw = ["1e16", "1", "1", "1", "-1e16"]
    print(f"Числа: {' '.join(raw)}")
    values = [float(x) for x in raw]
    true_value = sum(exact(x) for x in raw)

    results = {
        "в исходном порядке": naive_sum(values),
        "по возрастанию": naive_sum(sorted(values)),
        "по убыванию": naive_sum(sorted(values, reverse=True)),
        "по модулю (возр.)": naive_sum(sorted(values, key=abs)),
        "fsum": fsum(values),
        "Кэхэн": kahan_sum(values),
    }
    for name, r in results.items():
        print(f"{name:20}: {r!r}  погрешность={error(r, true_value)}")

    random.seed(1)
    sums = set()
    for _ in range(100):
        shuffled = values[:]
        random.shuffle(shuffled)
        sums.add(naive_sum(shuffled))
    print(f"Различных результатов при 100 перестановках: {len(sums)}")
    print(f"Точное значение: {float(true_value)!r}")


def task8():
    print("Вывод:")
    print("1. Большинство десятичных дробей (0.1, 0.2) не представимы в двоичном")
    print("   формате точно, поэтому уже исходные данные содержат погрешность.")
    print("2. Сложение во float не ассоциативно: результат зависит от порядка и")
    print("   группировки операций.")
    print("3. При сложении чисел разных масштабов малое слагаемое может быть")
    print("   потеряно полностью (потеря значимости).")
    print("4. Ошибки округления накапливаются при многократных операциях.")
    print("5. Для повышения точности используют сортировку слагаемых, суммирование")
    print("   Кэхэна (fsum), а также Decimal и Fraction.")
    print("6. Числа с плавающей точкой нельзя сравнивать через ==; используют")
    print("   сравнение с допуском (math.isclose).")
    print(f"Машинный эпсилон: {sys.float_info.epsilon}")


match int(input("Номер задания: ")):
    case 1:
        task1()
    case 2:
        task2()
    case 3:
        task3()
    case 4:
        task4()
    case 5:
        task5()
    case 6:
        task6()
    case 7:
        task7()
    case 8:
        task8()