【问题标题】:BigInt calculator spitting out slightly wrong resultsBigInt 计算器吐出稍微错误的结果
【发布时间】:2020-01-23 04:34:02
【问题描述】:

因此,对于我的作业,我必须创建一个可以处理最长 256 个字符的大整数的计算器。我要完成的任务的当前部分是让它与更大的数字相乘。 DIGITS 是每个 Bigint 类的位数限制,目前为调试目的设置为 20,但将增加到 256

在进行 25 * 137 之类的计算时,我得到的答案应该是 3425总和是 5 * 137,所以效果很好。但是,当它到达必须执行 i 循环的第二次迭代时,它是 20 * 137 时,它得到的答案是错误的,我无法弄清楚为什么。我有一种暗示,这与进位为两位数 (14) 有关,但我仍然无法真正弄清楚如何解决它。

明显有问题的主要实现在 bigint 类的 * 运算符中。我知道这与 > 运算符无关,因为它们非常适合加法和减法。

bigint 类的完整代码如下:

#include <iostream>
#include <string>
#include "Bigint.h"
#include <cmath>

using namespace std;

Bigint::Bigint()
{
    for (int i = DIGITS-1; i >= 0; --i) {
        digits_[i] = 0;
    }
}

ostream& operator<< (ostream& out, const Bigint& n)
{

    string s = "";
    bool found = false;
    for (int i = DIGITS - 1; i >= 0; --i) {
        if(n.digits_[i] > 0) {
            found = true;
        }
        if(n.digits_[i] != 0 || found == true) {
            s += char(n.digits_[i] + '0');
        }
    }
    if (s == "") {
        s = "0";
    }
    return out << s;
}

istream& operator>> (istream& in, Bigint& n)
{
    // Extracts full-length number (does not work for any other length).
    // All characters are assumed to be valid digits.
    //
    string s;
    if (in >> s) {
        for (int i = 0; i < DIGITS; ++i) {
            n.digits_[i] = i < s.length() ? s[s.length() - 1 - i] - '0' : 0;
        }
    }
    return in;
}

Bigint operator+ (const Bigint& n1, const Bigint& n2)
{
    Bigint ret;
    int cur_carry = 0;
    for(int i = 0; i < DIGITS; ++i) {
        int n1_digit = n1.get(i);
        int n2_digit = n2.get(i);
        if(n1_digit < 0 || n1_digit > 9) {
            n1_digit = 0;
        }
        if(n2_digit < 0 || n2_digit > 9) {
            n2_digit = 0;
        }
        //printf("n1 : %d\n", n1_digit);
        //printf("n2 : %d\n", n2_digit);
        int sum = n1_digit + n2_digit + cur_carry;
        //cout << "sum : " << sum << endl;
        cur_carry = Bigint::getCarry(sum);
        //cout << "new carry : " << cur_carry << endl;
        ret.set(i, Bigint::getDigitValue(sum));
        //cout << "Set : " << i << "," << Bigint::getDigitValue(sum) << endl;
    }
    return ret;
}

Bigint operator* (const Bigint& n1, const Bigint& n2)
{
    Bigint ret;
    //int borrowed = 0;
    Bigint sum;
    for(int i = 0; i < DIGITS ; i++){
        int n1_digit = n1.get(i);
        //cout << "n2: " << n2_digit << endl;
        Bigint temp;

        if(n1_digit < 0 || n1_digit > 9) {
            n1_digit = 0;
        }

        int carry = 0;

        for (int j = 0; j < DIGITS ; j++){
            int val = n1_digit * (pow(10, i)) * n2.get(j);
            cout << "n1: " << n1_digit << endl;
            cout << "n2: " << n2.get(j) << endl;

            if(carry != 0){
                temp.set(j, (Bigint::getDigitValue(val)) + carry);
                cout << "Carry was " << carry << ", now set 0" << endl;
                cout << "value to set: " << (Bigint::getDigitValue(val)) + carry << endl;
                carry = 0;
            }
            else if(carry == 0){
                temp.set(j, Bigint::getDigitValue(val));
                cout << "value to set: " << (Bigint::getDigitValue(val))<< endl;
            }

            carry = (Bigint::getCarry(val) + carry);
            cout << "carry: " << carry << endl;
        }
        cout << "Sum before adding temp: " << sum << endl;
        sum = sum + temp;
        cout << "Sum after adding temp: " << sum << endl;


    }

    ret = sum;
    return ret; // Only correct when n2 equals 1.
}

int Bigint::get(int pos) const {
    //Return address of digit for reading
    int ret = digits_[pos];
    return ret;
}

void Bigint::set(int pos, int val) {
    this->digits_[pos] = val;
}

int Bigint::getCarry(int val) {
    //Integer division, always floors
    return val/10;
}

int Bigint::getDigitValue(int val) {
    return val % 10;
}


头文件:


#ifndef BIGINT_H_
#define BIGINT_H_

#define DIGITS 20

class Bigint
{
  public:

    /**
     * Creates a Bigint initialised to 0.
     */
    Bigint();

    /**
     * Inserts n into stream or extracts n from stream.
     */
    friend std::ostream& operator<< (std::ostream &out, const Bigint& n);
    friend std::istream& operator>> (std::istream &in, Bigint& n);

    /**
     * Returns the sum, difference, product, or quotient of n1 and n2.
     */
    friend Bigint operator* (const Bigint& n1, const Bigint& n2);
    friend Bigint operator+ (const Bigint& n1, const Bigint& n2);

    int get(int pos) const;
    void set(int pos, int val);

    static int getCarry(int val);
    static int getDigitValue(int val);

  private:
    int digits_[DIGITS];
};

#endif // BIGINT_H_

主要:


#include <iostream>
#include "Bigint.h"

using namespace std;

int main(int argc, char *argv[]) 
{
    Bigint n1, n2;
    char op;

    while (cin >> n1 >> op >> n2) {
        switch (op) {
        case '+' :
            cout << n1 + n2 << endl;
            break;
        case '*' :
            cout << n1 * n2 << endl;
            break;
        }
    }



    return 0;
}
}

【问题讨论】:

  • 好的,抱歉,我已经尽可能减少代码量,只包含问题
  • 我可以让您对单元测试感兴趣吗?
  • 您是否尝试过在调试器中逐语句单步执行代码,同时监视变量及其值?我还建议您简化表达式,以便更容易看到即时结果。例如int val = n1_digit * (pow(10, i)) * n2.get(j) 可以拆分为int t1 = pow(10, i);int t2 = n1_digit * t1; int t3 = n2.get(j); int val = t2 * t3;`
  • @Someprogrammerdude 不在调试器中,但是,如果您要运行该代码,则会有一堆 cout 语句显示过程的每个步骤,并且携带两位数似乎是一个问题,因为例如,当 20 乘以 7 时,它是 140,所以它必须携带 14 并设置 0。除此之外,其他一切正常

标签: c++ arrays math element


【解决方案1】:

有一些潜在的问题

        for (int j = 0; j < DIGITS ; j++){
            int val = n1_digit * (pow(10, i)) * n2.get(j); // val % 10 == 0 for i > 0
            // You should also be adding the carry to val
            cout << "n1: " << n1_digit << endl;
            cout << "n2: " << n2.get(j) << endl;

            if(carry != 0){
                temp.set(j, (Bigint::getDigitValue(val)) + carry);
                // This can set temp[j] to values above 9 depending on the carry
                cout << "Carry was " << carry << ", now set 0" << endl;
                cout << "value to set: " << (Bigint::getDigitValue(val)) + carry << endl;
                carry = 0;
            }
            else if(carry == 0){
                temp.set(j, Bigint::getDigitValue(val));
                cout << "value to set: " << (Bigint::getDigitValue(val))<< endl;
            }

            carry = (Bigint::getCarry(val) + carry);
            cout << "carry: " << carry << endl;
        }

因为您乘以 10 的幂,所以 getDigitValue 和 getCarry 的行为与您期望的不同。将 temp.set 的索引移动 i 而不是乘以 pow(10, i) 可能会更好。

我还建议清理这些箱子。在这种情况下, if 和 else if 实际上都在做同样的工作,并且重置进位不会做任何事情。所以这将具有完全相同的行为:

        for (int j = 0; j < DIGITS ; j++){
            int val = n1_digit * (pow(10, i)) * n2.get(j);

            temp.set(j, (Bigint::getDigitValue(val)) + carry);
            // If the carry is 0, the addition doesn't do anything
            // You don't need to reset the carry to 0, since it's assigned here anyway
            carry = (Bigint::getCarry(val) + carry);
        }

没有 case 和 print 语句,阅读起来容易很多,但是这里使用方法(getCarry 和 getDigitValue)仍然很难通过将相关操作移到类的底部来发现问题。进行更改会给出:

        for (int j = 0; j < DIGITS ; j++){
            int val = n1_digit * n2.get(j) + carry;

            temp.set(i + j, Bigint::getDigitValue(val));
            carry = Bigint::getCarry(val);
            // The carry just gets added to the value at the beginning
            // Everything else just works that way
        }

您还需要更改 get 和 set 函数,使它们不会越界:

int Bigint::get(int pos) const {
    //Return address of digit for reading
    if (pos >= DIGITS)
        return 0;
    int ret = digits_[pos];
    return ret;
}

void Bigint::set(int pos, int val) {
    if (pos >= DIGITS)
        return ;
    this->digits_[pos] = val;
}

使用此代码,我得到 25 * 137 的 3425。

【讨论】:

  • 谢谢,这很有帮助,但只需将其转换为 ` temp.set(j + i, Bigint::getDigitValue(val)); ` 不起作用。我可以看到你的心态,但我不明白为什么它不起作用,可能填充数组的方式是另一种方式?额外的零需要在初始值放入数组之前出现
  • @Cloppy 似乎可能是因为您的 get 和 set 函数的内存访问越界,为每个测试用例添加边界检查得到正确的结果
【解决方案2】:

你不应该使用这条线int val = n1_digit * (pow(10, i)) * n2.get(j); 因为它会给出整数溢出,因为你正在使用 bigintger 而是使用乘数中的数字并在结果后面添加零。

要添加的零的数量取决于乘数的位置,您可以在重载的 * 函数中从此循环 for(int i = 0; i &lt; DIGITS ; i++) 中找到变量 i

【讨论】:

  • 是的,你是对的。但是我很难在数组中的现有数字之前插入零,因为它们必须在现有元素之前
  • 如果你先插入零怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 2018-01-05
相关资源
最近更新 更多