Home > Net >  Implementing banker's rounding C
Implementing banker's rounding C

Time:09-17

So essentially I'm doing a C book, and one of the exercises is to modify an example from the book to use banker's rounding. For context, banker's rounding is where in fractional cents, you round to the nearest even integer. I've been trying for hours to find out how to implement it, but nothing has worked for me. The code from the book is listed below.

// Ex. 5.31: DollarAmount.h
// DollarAmount class gets two parameter constructor
#include <string>
#include <cmath>

class DollarAmount {
public:
    // initialize amount from an int64_t value
    explicit DollarAmount(int64_t dollars, int64_t cents) : amount{dollars * 100   cents} { }

    // add right's amount to this object's amount
    void add(DollarAmount right) {
        // can access private data of other objects of the same class
        amount  = right.amount;
    }

    // subtract right's amount from this object's amount
    void subtract(DollarAmount right) {
        // can access private data of other objects of the same class
        amount -= right.amount;
    }

    // divide amount by the divisor
    void divide(int divisor) {
        amount = (amount   divisor / 2) / divisor;
    }

    // uses integer arithmetic to calculate interest amount,
    // then calls add with the interest amount
    void addInterest(int rate, int divisor) {
        // create DollarAmount representing the interest
        DollarAmount interest {
            ((amount * rate   divisor / 2) / divisor) / 100, // dollars
            ((amount * rate   divisor / 2) / divisor) % 100 // cents
        };

        add(interest); // add interest to this object's amount
    }

    // return a string representation of a DollarAmount object
    std::string toString() const {
        std::string dollars{std::to_string(amount / 100)};
        std::string cents{std::to_string(std::abs(amount % 100))};
        return dollars   "."   (cents.size() == 1 ? "0" : "")   cents;
    }
private:
    int64_t amount{0}; // dollar amount in pennies
};

I've tried several things but I reverted the code back to its original form since the other ones didn't work. The current algorithm uses normal rounding. The author didn't really explain the rounding system very well.

Edit: Here's also the main program used.

// Ex. 5.31: Interest.cpp
// Compound-interest calculations with class DollarAmount and integers.
#include <iostream>
#include <iomanip>
#include <string>
#include "DollarAmount.h"
using namespace std;

int main() {
    DollarAmount d1{123, 45}; // $123.45
    DollarAmount d2{15, 76}; // $15.76

    cout << "After adding d2 (" << d2.toString() << ") into d1 ("
        << d1.toString() << "), d1 = ";
    d1.add(d2); // modifies object d1
    cout << d1.toString() << "\n";

    cout << "After subtracting d2 (" << d2.toString() << ") into d1 ("
        << d1.toString() << "), d1 = ";
    d1.subtract(d2); // modifies object d1
    cout << d1.toString() << "\n";

    cout << "After subtracting d1 (" << d2.toString() << ") from d2 ("
        << d2.toString() << "), d2 = ";
    d2.subtract(d1); // modifies object d2
    cout << d2.toString() << "\n";

    cout << "After dividing d1 (" << d1.toString() << ") by 2, d1 = ";
    d1.divide(2); // modifies object d1
    cout << d1.toString() << "\n\n";
    
    cout << "Enter integer interest rate and divisor. For example:\n"
        << "for     2%, enter:    2 100\n"
        << "for   2.3%, enter:   23 1000\n"
        << "for  2.37%, enter:  237 10000\n"
        << "for 2.375%, enter: 2375 100000\n";
    int rate; // whole-number interest rate
    int divisor; // divisor for rate
    cin >> rate >> divisor;

    DollarAmount balance{1000, 0}; // initial principal amount in pennies
    cout << "\nInitial balance: " << balance.toString() << endl;

    // display headers
    cout << "\nYear" << setw(20) << "Amount on deposit" << endl;

    // calculate amount on deposit for each of ten years
    for (unsigned int year{1}; year <= 10; year  ) {
        // increase balance by rate % (i.e., rate / divisor)
        balance.addInterest(rate, divisor);

        // display the year and the amount
        cout << setw(4) << year << setw(20) << balance.toString() << endl;
    }
}

I'm currently testing with the inputs 2 100 and 5 1000000.

I've tried to make a conditional if the expression mod 1 is 0.5, and use the normal cents if it's true and uses the normal cents plus zero if the expression mod 2 is even and one if the expression mod 2 is odd. That ended up being messy and didn't even work and somehow got rid of all of the cents when I tested it. For the banker's rounding algorithm, inputting 2 100 should return 1218.98 for 10 years and 5 1000000 should always return 1000 because it's rounding to the nearest even integer, rounding down.

Edit 2: I've made some modifications to the code and I think I've thought of the algorithm I'll use. Here's the modified source code.

// Ex. 5.31: DollarAmount.h
// DollarAmount class gets two parameter constructor
#include <string>
#include <iostream>
#include <cmath>

class DollarAmount {
public:
    // initialize amount from an int64_t value
    explicit DollarAmount(int64_t dollars, int64_t cents) : amount{dollars * 100   cents} { }

    // add right's amount to this object's amount
    void add(DollarAmount right) {
        // can access private data of other objects of the same class
        amount  = right.amount;
    }

    // subtract right's amount from this object's amount
    void subtract(DollarAmount right) {
        // can access private data of other objects of the same class
        amount -= right.amount;
    }

    // divide amount by the divisor
    void divide(int divisor) {
        amount = (amount   divisor / 2) / divisor;
    }

    // uses integer arithmetic to calculate interest amount,
    // then calls add with the interest amount
    void addInterest(int rate, int divisor) {
        // create DollarAmount representing the interest
        DollarAmount interest {
            ((amount * rate   divisor / 2) / divisor) / 100, // dollars
            ((amount * rate   divisor / 2) / divisor) % 100 // cents
        };

        if (static_cast<int64_t>((((amount * rate   divisor / 2) / divisor) % 1) * 10) == 5) {
            if (interest.amount % 2 == 1) {
                interest.amount--;
            }
            
            std::cout << "test";
        }

        add(interest); // add interest to this object's amount
    }

    // return a string representation of a DollarAmount object
    std::string toString() const {
        std::string dollars{std::to_string(amount / 100)};
        std::string cents{std::to_string(std::abs(amount % 100))};
        return dollars   "."   (cents.size() == 1 ? "0" : "")   cents;
    }
private:
    int64_t amount{0}; // dollar amount in pennies
};

So essentially my plan is to just create the interest object normally, but then I check if the banker's rounding condition is met, and then if the interest.amount variable % 2 == 1, implying that it did not round to the nearest even integer. If this condition is met it deincrements the interest.amount variable. This seems to be a bit messy but since it doesn't seem like there is an easier way of doing it I think I'll be trying out this method. The problem is the if statement currently doesn't work that checks if banker's rounding is needed.

Edit 3: Added comments

// Ex. 5.31: DollarAmount.h
// DollarAmount class gets two parameter constructor
#include <string>
#include <iostream>
#include <cmath>

class DollarAmount {
public:
    // initialize amount from an int64_t value
    explicit DollarAmount(int64_t dollars, int64_t cents) : amount{dollars * 100   cents} { }

    // add right's amount to this object's amount
    void add(DollarAmount right) {
        // can access private data of other objects of the same class
        amount  = right.amount;
    }

    // subtract right's amount from this object's amount
    void subtract(DollarAmount right) {
        // can access private data of other objects of the same class
        amount -= right.amount;
    }

    // divide amount by the divisor
    void divide(int divisor) {
        amount = (amount   divisor / 2) / divisor;
    }

    // uses integer arithmetic to calculate interest amount,
    // then calls add with the interest amount
    void addInterest(int rate, int divisor) {
        // create DollarAmount representing the interest
        DollarAmount interest {
            ((amount * rate   divisor / 2) / divisor) / 100, // dollars
            ((amount * rate   divisor / 2) / divisor) % 100 // cents
        };

        // banker's rounding special case
        std::cout << (((amount * rate   divisor / 2) / divisor) % 1) * 10 << std::endl;
        if (static_cast<int64_t>((((amount * rate   divisor / 2) / divisor) % 1) * 10) == 5) {
            // if interest.amount is odd, implying normal rounding deviated from banker's rounding
            if (interest.amount % 2 == 1) {
                interest.amount--; // deincrement interest.amount to account for banker's rounding
            }

            std::cout << "test";
        }

        add(interest); // add interest to this object's amount
    }

    // return a string representation of a DollarAmount object
    std::string toString() const {
        std::string dollars{std::to_string(amount / 100)};
        std::string cents{std::to_string(std::abs(amount % 100))};
        return dollars   "."   (cents.size() == 1 ? "0" : "")   cents;
    }
private:
    int64_t amount{0}; // dollar amount in pennies
};

Edit 4: Okay, so I've figured out I will use normal rounding then check if bankers rounding is needed, then if it is and interest.amount is odd, implying it deviated from bankers rounding, it de-increments it. The only problem left is that my if statement that checks if bankers rounding is needed doesn't work.

CodePudding user response:

Here it goes:

// Ex. 5.31: DollarAmount.h
// DollarAmount class gets two parameter constructor
#include <string>
#include <cmath>
#include <iostream>

class DollarAmount {
public:
    // initialize amount from an int64_t value
    explicit DollarAmount(int64_t dollars, int64_t cents) : amount{dollars * 100   cents} { }

    // add right's amount to this object's amount
    void add(DollarAmount right) {
        // can access private data of other objects of the same class
        amount  = right.amount;
    }

    // subtract right's amount from this object's amount
    void subtract(DollarAmount right) {
        // can access private data of other objects of the same class
        amount -= right.amount;
    }

    // divide amount by the divisor
    void divide(int divisor) {
        amount = (amount   divisor / 2) / divisor;
    }

    // uses integer arithmetic to calculate interest amount,
    // then calls add with the interest amount
    void addInterest(int rate, int divisor) {
        // create DollarAmount representing the interest
        DollarAmount interest {
            ((amount * rate   divisor / 2) / divisor) / 100, // dollars
            ((amount * rate   divisor / 2) / divisor) % 100 // cents
        };

        add(interest); // add interest to this object's amount
    }

    // return a string representation of a DollarAmount object
    std::string toString() const {
        std::string dollars{std::to_string(amount / 100)};
        std::string cents{std::to_string(std::abs(amount % 100))};
        return dollars   "."   (cents.size() == 1 ? "0" : "")   cents;
    }

    void bankersRounding() {

        std::cout << "money: " << amount << " cents" << std::endl;
        int dollarsPart = amount/100;
        int penniesPart = amount%100;
        std::cout << " - dollars: " << dollarsPart << std::endl;
        std::cout << " - pennies: " << penniesPart << std::endl;

        // if it is equally distant from upper and lower integers,
        // then apply banker's rounding
        if (penniesPart==50)
        {
            // if the lower integer is zero, then round down to zero
            if(dollarsPart==0)
            {
                std::cout << "it is zero" << std::endl;
                amount -= 50;
            }
            // if the lower integer is even, then round down
            else if ((dollarsPart%2)==0)
            {
                std::cout << "even" << std::endl;
                amount -= 50;
            }
            // else the lower integer is odd, so round up
            else {
                std::cout << "odd" << std::endl;
                amount  = 50;
            }
        }
    }
private:
    int64_t amount{0}; // dollar amount in pennies

};


int main()
{
    DollarAmount d1(0, 0);
    DollarAmount d2(0, 50);

    d1.add(d2);

    d1.bankersRounding();

    std::cout << "final: " << d1.toString() << std::endl;

    return 0;
}

CodePudding user response:

Im not sure at which point youre stuck but maybe your are looking for something like this:

nearest_even=round(x/2)*2

I think thats a pretty simple implementation for "banker's rounding".

  • Related