【问题标题】:Implementing banker's rounding C++实现银行家的舍入 C++
【发布时间】:2021-11-07 19:15:52
【问题描述】:

所以本质上我正在写一本 C++ 书,其中一个练习是修改书中的一个示例以使用银行家的四舍五入。对于上下文,银行家的四舍五入是在分数美分中四舍五入到最接近的偶数。我已经尝试了几个小时来找出如何实现它,但没有任何东西对我有用。本书中的代码如下所示。

// 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
};

我已经尝试了几件事,但我将代码恢复为原始形式,因为其他代码不起作用。当前算法使用正常舍入。作者并没有真正很好地解释舍入系统。

编辑:这也是使用的主程序。

// 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;
    }
}

我目前正在使用输入 2 100 和 5 1000000 进行测试。

如果表达式 mod 1 为 0.5,我尝试设置条件,如果为真,则使用正常美分,如果表达式 mod 2 为偶数,则使用正常美分加零,如果表达式 mod 2 为奇数,则使用正常美分加零.这最终变得一团糟,甚至没有工作,并且在我测试它时以某种方式摆脱了所有的美分。对于银行家的四舍五入算法,输入 2 100 应该返回 1218.98 10 年,而 5 1000000 应该总是返回 1000,因为它是四舍五入到最接近的偶数,向下舍入。

编辑 2:我对代码进行了一些修改,我想我已经想到了我将使用的算法。这是修改后的源代码。

// 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
};

所以本质上我的计划是正常创建利息对象,然后我检查是否满足银行家的四舍五入条件,然后如果interest.amount变量%2 == 1,暗示它没有四舍五入最接近的偶数。如果满足此条件,它会减少 interest.amount 变量。这似乎有点混乱,但由于似乎没有更简单的方法可以做到这一点,我想我会尝试这种方法。问题是 if 语句目前无法检查是否需要银行家的四舍五入。

编辑 3:添加 cmets

// 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
};

编辑 4:好的,所以我想我将使用正常舍入,然后检查是否需要银行家舍入,然后如果需要并且 interest.amount 是奇数,这意味着它偏离了银行家舍入,它会减少它.剩下的唯一问题是我的 if 语句检查是否需要银行家四舍五入不起作用。

【问题讨论】:

  • 如果您能向我们展示您的尝试会更好,我们可以从那里提供帮助。向我们展示您尝试了哪些输入,您获得了什么结果以及您期望获得什么结果。也许你已经接近答案了,但仅仅给你答案对任何人都没有帮助。
  • 好的,我已经继续添加我尝试过的内容以及我正在测试的输入。我还包含了与类交互的主要功能。
  • 计算乘积而不除以divisor。取积 divisor 的余数,例如 r。比较 2*rdivisor。如果2*r 小于除数,则需要向下舍入。如果它等于除数,则需要四舍五入。如果它大于除数,则需要四舍五入。然后将乘积除以divisor 并截断,得到商q。如果四舍五入,什么也不做。如果四舍五入,则加 1。如果四舍五入,则将 q % 2 添加到 q。 (例如,如果q 为 3,则未截断的商为 3.5,因此您需要 4。)
  • (为了支持负数,可能需要对其进行一些修改。)
  • 抱歉,如果没有代码中的示例,我很难解释您的意思。由于练习要求我修改书中的一个示例,因此我不太了解程序中没有很好解释的某些部分,尤其是作者没有很好解释的舍入/逃学问题。 @EricPostpischil

标签: c++ c++11 c++14


【解决方案1】:

这里是:

// 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;
}

【讨论】:

  • 好吧,对于练习,应该修改 addInterest 成员函数。此外,它应该舍入分数美分,而不是分数美元。
  • 我注意到它只考虑美分,例如1 美元等于 100 美分。
  • 此实现不会将“金额”成员“更改为美元”。所以它仍然以美分运营,
  • 我计算美元部分和便士部分只是因为我更容易这样思考,因为它更自然或更典型的日常生活。可以通过许多其他方式来实现。
  • 因为0是偶数,所以代码中有一个没用的if。
【解决方案2】:

我不确定你在哪一点被卡住了,但也许你正在寻找这样的东西:

nearest_even=round(x/2)*2

我认为这是“银行家四舍五入”的一个非常简单的实现。

【讨论】:

  • 该代码如何将 2.5 舍入为 2 和 3.5 舍入为 4?
  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
  • round():返回最接近 x 的整数值,中间情况从零四舍五入(参见:cplusplus.com/reference/cmath/round)。所以 round(2.5/2) 返回 1 乘以 2 得到 2,而 round(3.5/2) 返回 2 乘以 2 得到 4。
  • @MaxHauptenbuchner 但是对于 2.5 和 3.5 之间的任何数字,例如 2.6,您的代码会给出 不正确的结果。会给出 2 而不是 3 或 3.1 会给出 4 而不是 3...
猜你喜欢
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多