Example 3: Fixed Point Number & Fixed Point Number Calculator

#include <iostream>
#include <string>
#include <cmath>

using namespace std;

class Fp_Calc; // forward declaration

// Fixed point class declaration
class Fixed_Point
{
private:
    long long int m_int_part;
    long long int m_frac_part;
    unsigned int m_precision;

    // utility functions
    // set fixed point without checking
    void _set(const string &arg_str);
    // normalize to m_precision
    void _normalize(const unsigned int arg_precision = 0);

public:
    Fixed_Point(const long long int &arg_int_part = 0,
                const unsigned long long int &arg_frac_part = 0,
                const unsigned int &arg_precision = 9);
    // Fixed_Point(const string &arg_str);

    ~Fixed_Point();

    // getter & setter
    void set_int_part(const long long int arg_int_part);
    void set_frac_part(const unsigned long long int arg_frac_part);
    void set_precision(const unsigned int arg_precision);
    void set(const long long int &arg_int_part,
             const unsigned long long int &arg_frac_part,
             const unsigned int &arg_precision);
    long long int get_int_part() const;
    unsigned long long int get_frac_part() const;
    unsigned int get_precision() const;
    string get_string_value() const;

    // arithmetic operators
    Fixed_Point operator+(const Fixed_Point &arg_fp) const;
    Fixed_Point operator-(const Fixed_Point &arg_fp) const;
    Fixed_Point operator+=(const Fixed_Point &arg_fp);
    Fixed_Point operator-=(const Fixed_Point &arg_fp);
    // conversion operators
    operator double() const;
    operator float() const;
    // assignment operators
    Fixed_Point &operator=(const Fixed_Point &arg_fp);
    Fixed_Point &operator=(const double &arg_double);
    Fixed_Point &operator=(const float &arg_float);

    // friend class
    friend class Fp_Calc;
    // friend function
    // formatted output
    friend ostream &operator<<(ostream &os, const Fixed_Point &arg_fp);
    // formatted input
    friend istream &operator>>(istream &is, Fixed_Point &arg_fp);
};

// Fixed point calculator class declaration
class Fp_Calc
{
private:
    // define current value
    Fixed_Point m_curr_val;
    // define input value
    Fixed_Point m_input_val;
    // define operation
    // `+`, `-`, `*`, `/`, and ` ` for none
    char m_op;
    // calculate result
    void _calc_result();

public:
    // Constructor
    Fp_Calc();
    // Copy constructor
    Fp_Calc(const Fp_Calc &arg_int_calc);

    // Destructor
    ~Fp_Calc(){}; // no need to do anything

    // clear the input value
    void clear();
    // // set the input value
    // void set_input(const string &arg_input);
    // add the input value to the current value
    void add();
    // subtract the input value from the current value
    void sub();
    // multiply the input value with the current value
    void mul();
    // divide the current value by the input value
    void div();
    // assign the input value to the current value
    void assign();
    // get the current value
    Fixed_Point get_curr_val();
    // get the current operation
    char get_op();
    // get the input value
    Fixed_Point get_input_val();

    // formatted output
    friend ostream &operator<<(ostream &os, const Fp_Calc &arg_fp_calc);
    // formatted input
    friend istream &operator>>(istream &is, Fp_Calc &arg_fp_calc);
};

// Fixed point class implementation

// utility functions

// set fixed point without checking
void Fixed_Point::_set(const string &arg_str)
{
    // set integer part
    m_int_part = stoll(arg_str.substr(0, arg_str.find('.')));
    // set fractional part
    m_frac_part = stoull(arg_str.substr(arg_str.find('.') + 1));
    // set precision
    m_precision = arg_str.length() - arg_str.find('.') - 1;
}

// normalize to precision
void Fixed_Point::_normalize(const unsigned int arg_precision)
{
    unsigned int temp_precision = arg_precision;
    // if precision is not specified, use the current precision
    if (arg_precision == 0)
    {
        temp_precision = m_precision;
    }
    // if precision is over the maximum, use the maximum
    else if (arg_precision > 9)
    {
        temp_precision = 9;
    }
    else
    {
        temp_precision = arg_precision;
    }

    // update fractional part
    int precision_diff = temp_precision - m_precision;
    if (precision_diff > 0)
    {
        m_frac_part *= pow(10, precision_diff);
    }
    else if (precision_diff < 0)
    {
        m_frac_part /= pow(10, -precision_diff);
    }

    // update m_precision
    m_precision = temp_precision;

    // handle fractional part overflow

    // update integer part
    if (m_frac_part >= pow(10, m_precision))
    {
        m_int_part += m_frac_part / pow(10, m_precision);
        m_frac_part %= (int)pow(10, m_precision);
    }

    // handle fractional part underflow

    // update integer part
    if (m_frac_part < 0)
    {
        m_int_part -= 1;
        m_frac_part += pow(10, m_precision);
    }
}

// Constructor
Fixed_Point::Fixed_Point(const long long int &arg_int_part,
                         const unsigned long long int &arg_frac_part,
                         const unsigned int &arg_precision)
    : m_int_part(arg_int_part),
      m_frac_part(arg_frac_part),
      m_precision(arg_precision)
{
}

Fixed_Point::~Fixed_Point()
{
    // do nothing
}

// getter & setter
void Fixed_Point::set_int_part(const long long int arg_int_part)
{
    m_int_part = arg_int_part;
}
void Fixed_Point::set_frac_part(const unsigned long long int arg_frac_part)
{
    m_frac_part = arg_frac_part;
    _normalize();
}
void Fixed_Point::set_precision(const unsigned int arg_precision)
{
    _normalize(arg_precision);
}
void Fixed_Point::set(const long long int &arg_int_part,
                      const unsigned long long int &arg_frac_part,
                      const unsigned int &arg_precision)
{
    m_int_part = arg_int_part;
    m_frac_part = arg_frac_part;
    m_precision = arg_precision;
    _normalize();
}

long long int Fixed_Point::get_int_part() const
{
    return m_int_part;
}
unsigned long long int Fixed_Point::get_frac_part() const
{
    return m_frac_part;
}
unsigned int Fixed_Point::get_precision() const
{
    return m_precision;
}
string Fixed_Point::get_string_value() const
{
    string str_int_part = to_string(m_int_part);
    string str_frac_part = to_string(m_frac_part);
    // if precision is not specified, use the current precision
    if (m_precision == 0)
    {
        return str_int_part;
    }
    // if precision is 1, return integer part and fractional part
    else if (m_precision == 1)
    {
        return str_int_part + "." + str_frac_part;
    }
    // if precision is over 1, return integer part and fractional part with
    // specified precision
    else
    {
        // if fractional part is 0, return integer part
        if (str_frac_part == "0")
        {
            return str_int_part;
        }
        // if fractional part is not 0, return integer part and fractional part
        else
        {
            // if fractional part is less than precision, add 0 to the front
            if (str_frac_part.length() < m_precision)
            {
                str_frac_part = string(m_precision - str_frac_part.length(), '0') 
                                + str_frac_part;
            }
            // if fractional part is more than precision, remove the end
            else if (str_frac_part.length() > m_precision)
            {
                str_frac_part = str_frac_part.substr(0, m_precision);
            }
            return str_int_part + "." + str_frac_part;
        }
    }
}

Fixed_Point Fixed_Point::operator+(const Fixed_Point &arg_fixed_point) const
{
    Fixed_Point result(*this);
    result += arg_fixed_point;
    return result;
}

Fixed_Point Fixed_Point::operator-(const Fixed_Point &arg_fixed_point) const
{
    Fixed_Point result(*this);
    result -= arg_fixed_point;
    return result;
}

Fixed_Point Fixed_Point::operator+=(const Fixed_Point &arg_fixed_point)
{
    Fixed_Point normalized_arg(arg_fixed_point);
    unsigned int max_precision = max(m_precision,
                                     normalized_arg.m_precision);
    set_precision(max_precision);
    normalized_arg.set_precision(max_precision);
    m_precision = max_precision;
    m_int_part = m_int_part + normalized_arg.m_int_part;
    m_frac_part = m_frac_part + normalized_arg.m_frac_part;
    _normalize();
    return *this;
}

Fixed_Point Fixed_Point::operator-=(const Fixed_Point &arg_fixed_point)
{
    Fixed_Point normalized_arg(arg_fixed_point);
    unsigned int max_precision = max(m_precision,
                                     normalized_arg.m_precision);
    set_precision(max_precision);
    normalized_arg.set_precision(max_precision);
    m_int_part = m_int_part - normalized_arg.m_int_part;
    m_frac_part = m_frac_part - normalized_arg.m_frac_part;
    _normalize();
    return *this;
}

Fixed_Point::operator double() const
{
    return double(m_int_part) + double(m_frac_part) / pow(10, m_precision);
}

Fixed_Point::operator float() const
{
    return float(m_int_part) + float(m_frac_part) / pow(10, m_precision);
}

Fixed_Point &Fixed_Point::operator=(const Fixed_Point &arg_fp)
{
    m_int_part = arg_fp.m_int_part;
    m_frac_part = arg_fp.m_frac_part;
    m_precision = arg_fp.m_precision;
    return *this;
}
Fixed_Point &Fixed_Point::operator=(const double &arg_double)
{
    _set(to_string(arg_double));
    _normalize();
    return *this;
}
Fixed_Point &Fixed_Point::operator=(const float &arg_float)
{
    _set(to_string(arg_float));
    _normalize();
    return *this;
}

// formated output
ostream &operator<<(ostream &arg_os, const Fixed_Point &arg_fixed_point)
{
    arg_os << arg_fixed_point.get_string_value(); // reuse get_string_value()
    return arg_os;
}

// formated input
istream &operator>>(istream &arg_is, Fixed_Point &arg_fixed_point)
{
    string str_value;
    arg_is >> str_value;
    arg_fixed_point._set(str_value); // reuse _set()
    arg_fixed_point._normalize();
    return arg_is;
}

// Fp_Calc class implementation

// Constructor
Fp_Calc::Fp_Calc()
    : m_curr_val(), m_input_val(), m_op(' ')
{
}

// Copy constructor
Fp_Calc::Fp_Calc(const Fp_Calc &arg_int_calc)
    : m_curr_val(arg_int_calc.m_curr_val),
      m_input_val(arg_int_calc.m_input_val),
      m_op(arg_int_calc.m_op)
{
}

// calculate the result
void Fp_Calc::_calc_result()
{
    switch (m_op)
    {
    case '+':
        m_curr_val += m_input_val;
        break;
    case '-':
        m_curr_val -= m_input_val;
        break;
    case ' ':
        m_curr_val = m_input_val;
        break;
    default:
        break;
    }
}

// clear the input value
void Fp_Calc::clear()
{
    m_input_val = Fixed_Point();
}

// // set the input value
// void Fp_Calc::set_input(const string &arg_input_val)
// {
//     m_input_val = Fixed_Point(arg_input_val);
// }

// add the input value to the current value
void Fp_Calc::add()
{
    _calc_result();
    m_op = '+';
}

// subtract the input value from the current value
void Fp_Calc::sub()
{
    _calc_result();
    m_op = '-';
}

// multiply the input value with the current value
void Fp_Calc::mul()
{
    _calc_result();
    m_op = '*';
}

// divide the current value by the input value
void Fp_Calc::div()
{
    _calc_result();
    m_op = '/';
}

// assign the input value to the current value
void Fp_Calc::assign()
{
    _calc_result();
    m_op = ' ';
}

// get the current value
Fixed_Point Fp_Calc::get_curr_val()
{
    return m_curr_val;
}

// get the current operation
char Fp_Calc::get_op()
{
    return m_op;
}

// get the input value
Fixed_Point Fp_Calc::get_input_val()
{
    return m_input_val;
}

// formated output
ostream &operator<<(ostream &arg_os, Fp_Calc &arg_fp_calc)
{
    arg_os << arg_fp_calc.get_curr_val();
    return arg_os;
}

// formated input
istream &operator>>(istream &arg_is, Fp_Calc &arg_fp_calc)
{
    arg_is >> arg_fp_calc.m_input_val;
    return arg_is;
}

// main function

int main()
{
    // create an instance of the class
    Fp_Calc calc;
    // set the input value
    cin >> calc;
    // get the input value
    cout << "Input: " << calc.get_input_val() << endl;
    // add the input value to the current value
    calc.add();
    // get current op
    cout << '\'' << calc.get_op() << '\'' << endl;
    // set another input value
    cin >> calc;
    // get the input value
    cout << "Input: " << calc.get_input_val() << endl;
    // clear the input value
    calc.clear();
    cout << "clear" << endl;
    // set the input value
    cin >> calc;
    // get the input value
    cout << "Input: " << calc.get_input_val() << endl;
    // subtract the input value from the current value
    calc.sub();
    // get current op
    cout << '\'' << calc.get_op() << '\'' << endl;
    // print the result
    cout << "Result: " << calc << endl;
    // set another input value
    cin >> calc;
    // get the input value
    cout << "Input: " << calc.get_input_val() << endl;
    // compute the result
    calc.assign();
    // print the result
    cout << "Result: " << calc << endl;
}