Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Решения лабораторных работ (местами есть ошибки) / Lab1_Rational numbers (нужно допилить, отрицательные числа не сработают)

.cpp
Скачиваний:
38
Добавлен:
10.09.2019
Размер:
6.16 Кб
Скачать
#include <iostream>
#include <cmath>
#include <stdexcept>

using std::cin;
using std::cout;
using std::endl;

// Класс «Рациональное число»
class Rational {
 protected:

    unsigned long long ulong_abs(long long arg) { // Модуль arg
        if (arg < 0) { return -arg; }
        return arg;
    }

    Rational gcdNormalize() { // Нормализация числа при помощи алгоритма Евклида
        unsigned long long A = ulong_abs(p), B = ulong_abs(q);
        while (A != 0 && B != 0) {
            if (A > B) {
                A %= B;
            } else {
                B %= A;
            }
        }
        if (A + B != 0) {
            p /= ( A + B ), q /= ( A + B );
        } else {
            p = 0, q = 1;
        }
        return *this;
    }

 public:
    long long p, q;

    // Конструктор
    explicit Rational(long long p = 0, long long q = 1) : p(p), q(q) {
        if (q != 0) {
            gcdNormalize();
            return;
        }
        throw std::invalid_argument("Q == 0!");
    }

    // Сложение рациональных чисел
    Rational operator+(const Rational &arg) const {
        return Rational(p * arg.q + arg.p * q, q * arg.q).gcdNormalize();
    }

    // Вычитание рациональных чисел
    Rational operator-(const Rational &arg) const {
        return Rational(p * arg.q - arg.p * q, q * arg.q).gcdNormalize();
    }

    // Умножение рациональных чисел
    Rational operator*(const Rational &arg) const {
        Rational temp(p * arg.p, q); // Умножаем только числитель и затем
        temp.gcdNormalize(); // Нормализуем число, чтобы уменьшить вероятность переполнения при следующей операции
        return Rational(temp.p, temp.q * arg.q).gcdNormalize();
    }

    // Деление рациональных чисел
    Rational operator/(const Rational &arg) const {
        Rational temp(p * arg.q, q); // Умножаем только числитель и затем
        temp.gcdNormalize(); // Нормализуем число, чтобы уменьшить вероятность переполнения при следующей операции
        return Rational(temp.p, temp.q * arg.p).gcdNormalize();
    }

    // Проверка на равенство
    bool operator==(const Rational &arg) const {
        return ( toDouble() == arg.toDouble() );
    }

    // Проверка на неравенство
    bool operator!=(const Rational &arg) const {
        return ( toDouble() != arg.toDouble() );
    }

    // Больше
    bool operator>(const Rational &arg) const {
        return ( toDouble() > arg.toDouble() );
    }

    // Меньше
    bool operator<(const Rational &arg) const {
        return ( toDouble() < arg.toDouble() );
    }

    // Больше или равно
    bool operator>=(const Rational &arg) const {
        return ( toDouble() >= arg.toDouble() );
    }

    // Меньше или равно
    bool operator<=(const Rational &arg) const {
        return ( toDouble() <= arg.toDouble() );
    }

    // Оператор приведения к типу bool
    explicit operator bool() {
        return p != 0;
    }

    // Преобразование в вещественное число
    double toDouble() const {
        return double(p) / double(q);
    }

    // Дружественный оператор для вывода рационального числа в выходной поток os (cout, ...)
    friend std::ostream &operator<<(std::ostream &os, const Rational &arg) {
        return os << arg.p << '/' << arg.q;
    }
};

// Рекуррентное соотношение Мюллера. Тест чисел типа Rational и типа Double
void MuellerRecurrenceRatioTest() {
    double x0d = 4, x1d = 4.25;
    Rational x0r(4, 1), x1r(17, 4);
    cout << "I\tDouble\t\tRational\n";
    const int N = 13; // N > 13: теряется точность у Rational
    for (size_t i = 1; i <= N; ++i) { // Цикл вычисления по Rational и по Double
        double tempD = x1d;
        x1d = 108. - 815. / x1d + 1500. / x0d / x1d;
        x0d = tempD;
        Rational tempR = x1r;
        x1r = Rational(108) - ( Rational(815) - Rational(1500) / x0r ) / x1r;
        x0r = tempR;
        cout << i << '\t' << x1d << "\t\t" << x1r.toDouble() << endl;
    }
    cout << "[Incorrect answer] Double result: " << x1d << endl;
    cout << "[Almost correct answer] Rational result: " << x1r << " (" << x1r.toDouble() << ")\n";
}

int main() {
    Rational a(2, 3), b(1, 3);
    // Простейшие операции с рациональными числами
    cout << "A: " << a << " (" << a.toDouble() << ")\n";
    cout << "B: " << b << " (" << b.toDouble() << ")\n";
    cout << "A + B: " << a + b << " (" << ( a + b ).toDouble() << ")\n";
    cout << "A - B: " << a - b << " (" << ( a - b ).toDouble() << ")\n";
    cout << "A * B: " << a * b << " (" << ( a * b ).toDouble() << ")\n";
    cout << "A / B: " << a / b << " (" << ( a / b ).toDouble() << ")\n";
    Rational c(123524, 153524), d(4522, 1232);
    cout << "C: " << c << " (" << c.toDouble() << ")\n";
    cout << "D: " << d << " (" << d.toDouble() << ")\n";
    // Отношения рациональных чисел
    cout << "A == B ? " << ( a == b ? "TRUE" : "FALSE" ) << endl;
    cout << "A != B ? " << ( a != b ? "TRUE" : "FALSE" ) << endl;
    cout << "A > B ? " << ( a > b ? "TRUE" : "FALSE" ) << endl;
    cout << "A >= B ? " << ( a >= b ? "TRUE" : "FALSE" ) << endl;
    cout << "A < B ? " << ( a < b ? "TRUE" : "FALSE" ) << endl;
    cout << "A <= B ? " << ( a <= b ? "TRUE" : "FALSE" ) << endl;
    // Рекуррентное соотношение Мюллера
    MuellerRecurrenceRatioTest();
}