l

lll
Last edited on
Comparing floating point numbers is not easy. Usually, you want to verify if their difference is below a value you can tolerate. If you sum a bunch of them and than compare that value with another sum of floating point numbers, that comparison could overcome your tolerance.
Also, when you save in a text file floating point numbers setting little precisions, you should consider you could face a possible loss of data (their fractional part will be truncate while reading them).

The following basic example works, but very often complains for discrepancies in values: that’s because, when the precision is little, what you see can be far from what you have. To get to the output below, I had to choose the data carefully.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// How do I read in the data from a file and put it into a two dimensional
// array and them sum all the values in the principle paid and interest paid
// columns and compare them to the running totals I calculated.
#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

struct TableDtls {
    double monthly_rate             {};
    int num_months                  {};
    double monthly_payment          {};
    double monthly_interest_amount  {};
    double monthly_principle_amount {};
    double running_interest_total   {};
    double running_principle_total  {};
    double new_balance              {};
    double unpaid_balance           {};
    void setToCalc();
};

void TableDtls::setToCalc()
{
    monthly_interest_amount  = 0.0;
    monthly_principle_amount = 0.0;
    running_interest_total   = 0.0;
    running_principle_total  = 0.0;
    new_balance              = 0.0;
}

struct Loan {
    using table = std::tuple<double, double, double, double, double, double, double>;
    double lprinc   {}, // loan amount
           yint     {}, // year-based interest rate
           term     {}; // duration left
    TableDtls det;
    std::vector<table> amm;
    double calcMonthPay();
    void calcTable();
    bool operator==(const Loan& other) const;
    bool operator!=(const Loan& other) const;
    friend std::ofstream& operator<<(std::ofstream& os, const Loan& rhs)
    {
        os << rhs.lprinc << ' ' << rhs.yint << ' ' << rhs.term  << '\n';
        for(size_t i{}; i<rhs.amm.size(); ++i) {
            os << std::right << std::setw(6) << i;
            const auto& t = rhs.amm.at(i);
            os << std::right << std::setw(22) << std::get<0>(t)
               << std::right << std::setw(20) << std::get<1>(t)
               << std::right << std::setw(17) << std::get<2>(t)
                             << std::setw(19) << std::get<3>(t)
               << std::right << std::setw(18) << std::get<4>(t)
               << std::right << std::setw(15) << std::get<5>(t)
               << std::right << std::setw(23) << std::get<6>(t) << '\n';
        }
        return os;
    }
    friend std::ostream& operator<<(std::ostream& os, const Loan& rhs)
    {
        std::cout << "Payment #   Mortgage Loan Balance  Mortgage Payment "
                    " Principle Amount   Principle Paid   Interest Amount  "
                    " Interest Paid    New Mortgage Balance\n\n";
        os << rhs.lprinc << ' ' << rhs.yint << ' ' << rhs.term  << '\n';
        for(size_t i{}; i<rhs.amm.size(); ++i) {
            os << std::right << std::setw(6) << i;
            const auto& t = rhs.amm.at(i);
            os << std::right << std::setw(22) << std::get<0>(t)
               << std::right << std::setw(20) << std::get<1>(t)
               << std::right << std::setw(17) << std::get<2>(t)
                             << std::setw(19) << std::get<3>(t)
               << std::right << std::setw(18) << std::get<4>(t)
               << std::right << std::setw(15) << std::get<5>(t)
               << std::right << std::setw(23) << std::get<6>(t) << '\n';
        }
        return os;
    }
    friend std::ifstream& operator>>(std::ifstream& is, Loan& rhs)
    {
        int lines {};
        for(std::string line; std::getline(is, line); ++lines) {
            if(line.empty()) { continue; }
            std::istringstream iss(line);
            if(!lines) { iss >> rhs.lprinc >> rhs.yint >> rhs.term; continue; }
            double a, b, c, d, e, f, g, h; // on the file there are 8 columns
            iss >> a >> b >> c >> d >> e >> f >> g >> h;
            rhs.amm.push_back(std::make_tuple(b, c, d, e, f, g, h));
        }
        return is;
    }
};

// throws std::out_of_range if denominator result 0
double Loan::calcMonthPay()
{
    det.monthly_rate = yint / 12.0;
    // calculate year fraction in months:
    double ipart {};
    double frac = 10.0 * std::modf(term, &ipart);
    int coeff = static_cast<int>(std::round(10 / frac));
    det.num_months = 12 * ipart + 12 / coeff;
    double numerator = det.monthly_rate * pow((1.0 + det.monthly_rate), det.num_months);
    double denominator = pow((1.0 + det.monthly_rate), det.num_months) - 1;
    if (denominator == 0) { throw std::out_of_range("Denominator is 0!"); }
    det.monthly_payment = lprinc * (numerator / denominator);
    return det.monthly_payment;
}

void Loan::calcTable()
{
    det.unpaid_balance = lprinc;
    det.setToCalc();
    for (int payment = 1; payment <= det.num_months; payment++) {
        det.monthly_interest_amount = det.unpaid_balance * det.monthly_rate;
        det.monthly_principle_amount =   det.monthly_payment
                                       - det.monthly_interest_amount;
        det.new_balance =  det.unpaid_balance - det.monthly_principle_amount;
        det.running_interest_total += det.monthly_interest_amount;
        det.running_principle_total += det.monthly_principle_amount;

        amm.push_back(std::make_tuple(det.unpaid_balance,
                                      det.monthly_payment,
                                      det.monthly_principle_amount,
                                      det.running_principle_total,
                                      det.monthly_interest_amount,
                                      det.running_interest_total,
                                      det.new_balance) );

        det.unpaid_balance = det.new_balance;
    }
}

bool Loan::operator==(const Loan& other) const
{
    if(this == &other) { return true; } // check self comparison

    if(lprinc != other.lprinc) { return false; }
    if(yint   != other.yint)   { return false; }
    if(term   != other.term)   { return false; }

    // data inside det are just needed for computation and are irrelevant

    if(amm.size() != other.amm.size()) { return false; }
    for(size_t i {}; i < amm.size(); ++i) {
        const auto& t1 = amm.at(i);
        const auto& t2 = other.amm.at(i);
        // choose a reasonable discrepancy here -----------\/
        if(std::fabs(std::get<0>(t1) - std::get<0>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<1>(t1) - std::get<1>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<2>(t1) - std::get<2>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<3>(t1) - std::get<3>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<4>(t1) - std::get<4>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<5>(t1) - std::get<5>(t2)) > 0.01) { return false; }
        if(std::fabs(std::get<6>(t1) - std::get<6>(t2)) > 0.01) { return false; }
    }
    return true;
}

bool Loan::operator!=(const Loan& other) const { return !(*this == other); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
int getNumFromUser(int min, int max);
double getNumFromUser(double min, double max);
void loanCalculator(Loan& loan, const std::string& name);
void differenceCalculator(const Loan& loan);
void waitForEnter();

int main()
{
    std::cout << "Please enter your fullname (e.g., John Q. Public): ";
    std::string fname, mname, lname;
    std::cin >> fname >> mname >> lname;
    std::string fullname = fname + ' ' + lname;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    Loan loan;
    int opt {};
    do {
        std::cout << "Do you want to:\n1) calculate a loan monthly rate"
                     "\n2) load data from file?\n3) exit?\n>>> ";
        opt = getNumFromUser(1, 3);
        switch(opt) {
        case 1:
            loanCalculator(loan, fullname);
            break;
        case 2:
            differenceCalculator(loan);
            break;
        case 3: // just exit
            break;
        }
    } while(0 < opt && opt < 3);
}

int getNumFromUser(int min, int max)
{
    for(std::string line; std::getline(std::cin, line); /**/) {
        int i {};
        try {
            i = std::stoi(line);
            if(i < min || max < i) {
                std::cout << "Expected number ranges from " << min << " to "
                          << max << ".\nPlease try again.\n>>> ";
                continue;
            }
            return i;
        } catch(std::logic_error& e) {
            std::cout << "Please insert digits [0-9].\nTry again.\n>>> ";
        }
    }
}

double getNumFromUser(double min, double max)
{
    for(std::string line; std::getline(std::cin, line); /**/) {
        double d {};
        try {
            d = std::stod(line);
            if(d < min || max < d) {
                std::cout << "Expected number ranges from " << min << " to "
                          << max << ".\nPlease try again.\n>>> ";
                continue;
            }
            return d;
        } catch(std::logic_error& e) {
            std::cout << "Please insert digits [0-9].\nTry again.\n>>> ";
        }
    }
}

void loanCalculator(Loan& loan, const std::string& name)
{
    std::string loan_type { "mortgage" };
    bool valid_values { false };
    do
    {
        std::cout << "\nHow much is your " << loan_type << " loan amount (e.g., "
                     "no commas or dollar sign please): $";
        // Note: set reasonable limits each time you call this function
        loan.lprinc = getNumFromUser(1.0, std::numeric_limits<double>::max());
        std::cout << "\nWhat is the annual interest rate (e.g., please enter "
                     "percentage as a decimal): ";
        loan.yint = getNumFromUser(0.01, 20.0);
        std::cout << "\nHow long is the term of this " << loan_type
                  << " loan (e.g., a decimal value is OK): ";
        loan.term = getNumFromUser(0.5, 30.0);
        // calculate the monthly payment
        try {
            loan.calcMonthPay();
            valid_values = true;
        } catch(std::out_of_range& e) {
            std::cout << "Sorry, can't calculate monthly payment from you data."
                         "\nReason: " << e.what()
                      << "\nPlease try again.\n";
            continue;
        }
    } while (valid_values == false);

    std::cout << name << ", your monthly payment for\n - a " << loan_type
              << " loan balance of " << loan.lprinc
              << "\n - with an interest rate of "
              << std::fixed << std::setprecision(4) << loan.yint
              << "\n - for a term of " << loan.det.num_months << " months\nwill be $"
              << std::fixed << std::setprecision(2) << loan.det.monthly_payment
              << "\n\nWould you like to see a complete amortization table for this "
              << loan_type << " loan? (y/n): ";
    char answ {};
    std::cin >> answ;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    if (answ == 'y' || answ == 'Y') {
        loan.calcTable();
        std::cout << loan;
        std::cout << "\nWould you like to send this output to a file (y/n)? ";
        std::cin >> answ;
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        if (answ == 'y' || answ == 'Y') {
            std::ofstream outputFile(name + ".txt");
            if(!outputFile) {
                std::cout << "Can't open output file.\n";
                return;
            }
            outputFile << loan << '\n';
            outputFile.close();
            std::cout << "\nThe " << loan_type << " amortization table has been "
                         "written to the " << name << ".txt file.\n";
            
        }
    }
    waitForEnter();
}

void differenceCalculator(const Loan& loan)
{
    std::cout << "Would you like to verify your amortization table (Y/N)? ";
    char answ;
    std::cin >> answ;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    if (answ != 'Y' && answ != 'y') {
        std::cout << "Returning to main menu. \n";
        return;
    }
    std::cout << "Please enter the file name: ";
    std::string file_name;
    std::getline(std::cin, file_name);
    std::ifstream infile(file_name);
    if(!infile) {
        std::cout << "Can't open " << file_name << ". Closing procedure.\n";
        return;
    }
    Loan tmploan;
    infile >> tmploan;
    infile.close();
    if(tmploan == loan) {
        std::cout << "Your amortisation table is fine.\n";
    } else {
        std::cout << "There are discrepancies (saved in dump.txt).\n";
        std::ofstream of("dump.txt");
        of << tmploan;
        of.close();
        for(size_t i {}; i<loan.amm.size(); ++i) {
            std::cout << "Elements in memory - line " << i << ": ";
            {
                const auto& t = loan.amm.at(i);
                std::cout << std::get<0>(t) << ' ';
                std::cout << std::get<1>(t) << ' ';
                std::cout << std::get<2>(t) << ' ';
                std::cout << std::get<3>(t) << ' ';
                std::cout << std::get<4>(t) << ' ';
                std::cout << std::get<5>(t) << ' ';
                std::cout << std::get<6>(t) << ' ';
            }
            std::cout << '\n';
            std::cout << "Elements on file - line " << i << ":   ";
            {
                const auto& t = tmploan.amm.at(i);
                std::cout << std::get<0>(t) << ' ';
                std::cout << std::get<1>(t) << ' ';
                std::cout << std::get<2>(t) << ' ';
                std::cout << std::get<3>(t) << ' ';
                std::cout << std::get<4>(t) << ' ';
                std::cout << std::get<5>(t) << ' ';
                std::cout << std::get<6>(t) << ' ';
            }
            std::cout << '\n';
            std::cout << "Press 'x' to exit or any other key for next line: ";
            char c;
            std::cin >> c;
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            if(c == 'x') { break; }
        }
    }
    waitForEnter();
}

void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Output:
Please enter your fullname (e.g., John Q. Public): John Q. Public
Do you want to:
1) calculate a loan monthly rate
2) load data from file?
3) exit?
>>> 1

How much is your mortgage loan amount (e.g., no commas or dollar sign please): $10000.00

What is the annual interest rate (e.g., please enter percentage as a decimal): 2.5

How long is the term of this mortgage loan (e.g., a decimal value is OK): .5
John Public, your monthly payment for
 - a mortgage loan balance of 10000
 - with an interest rate of 2.5000
 - for a term of 6 months
will be $3069.49

Would you like to see a complete amortization table for this mortgage loan? (y/n): y
Payment #   Mortgage Loan Balance  Mortgage Payment  Principle Amount   Principle Paid   Interest Amount   Interest Paid    New Mortgage Balance

10000.00 2.50 0.50
     0              10000.00             3069.49           986.16             986.16           2083.33        2083.33                9013.84
     1               9013.84             3069.49          1191.61            2177.76           1877.88        3961.22                7822.24
     2               7822.24             3069.49          1439.86            3617.62           1629.63        5590.85                6382.38
     3               6382.38             3069.49          1739.83            5357.44           1329.66        6920.51                4642.56
     4               4642.56             3069.49          2102.29            7459.73            967.20        7887.71                2540.27
     5               2540.27             3069.49          2540.27           10000.00            529.22        8416.94                  -0.00

Would you like to send this output to a file (y/n)? y

The mortgage amortization table has been written to the John Public.txt file.

Press ENTER to continue...

Do you want to:
1) calculate a loan monthly rate
2) load data from file?
3) exit?
>>> 2
Would you like to verify your amortization table (Y/N)? y
Please enter the file name: John Public.txt
Your amortisation table is fine.

Press ENTER to continue...

okay so I have updated the code but the assignment I have is kind of weird if you'd like private message me and I'll send you the full code as long as what the code needs to do in the end. I have been struggling with it all day long.
but the assignment I have

Asking about assignments is explicitely forbidden in this forum:
http://www.cplusplus.com/forum/beginner/1/
Don't post homework questions
Programmers are good at spotting homework questions; most of us have done them ourselves. Those questions are for you to work out, so that you will learn from the experience. It is OK to ask for hints, but not for entire solutions.

Next time please take care to specify you’re asking about an assignment.

if you'd like private message me and I'll send you the full code as long as what the code needs to do in the end

I can live without :-)
The answers given on the forum:
- are corrected by other users, most of whom are more reliable than me
- could turn out to be useful for other people, besides the OP.
Conclusion: it’s better to post on the forum.
Topic archived. No new replies allowed.