0

I've written code that works pretty well except for one thing. The task that I'm making this code for inputs data into the program as a whitespace seperated string of doubles. And their precicion may be larger than 10^-25. So I made my own class that can handle that.

The problem is, when I was writing the code, I was testing it by entering two values into the console by hand each time pressing enter so that my program can understand where one double ends and another starts (it was looking for a '\n' basically).

Now I really need to adapt this code to make i work with my task's input (whitespace seperated list of doubles like 2.521 32.12334656 23.21 .....). But I'm having a problem with getline in my overloaded >> operator. It simply eats the '\n' character and starts looking for more input. The only way I can get it to work is by manually enetering values and manually entering an additional whitespace after the last value and only then hit enter.

I'm asking for your help.

Here's the full code:

#include <iostream>
#include <string>
#include <algorithm>


class BigNumber {
private:
    std::string fullPart;
    std::string floatPart;

public:
    BigNumber() : fullPart("0"), floatPart("0") {}


    friend std::ostream & operator << (std::ostream & os, const BigNumber & bn);
    friend std::istream & operator >> (std::istream & os, BigNumber & bn);

    void operator+=(BigNumber & bn);
};

int main()
{
    BigNumber bn, bntemp;

    while (std::cin >> bntemp)
    {
        bn += bntemp;

        if (std::cin.peek() == '\n')
            break;
    }

    std::cout << bn << std::endl;
    return 0;
}

void addFullPart(const std::string & add, std::string & add_to)
{
    auto addConv = std::stold(add);
    auto addToConv = std::stold(add_to);

    auto newFull = std::to_string(addConv + addToConv);
    add_to = std::string(newFull.begin(), std::find(newFull.begin(), newFull.end(), '.'));
}

bool carryReminder(std::string & add_to, int32_t indx_from)
{
    for (auto curr = indx_from; curr >= 0; --curr)
    {
        if (add_to[curr] != '9')
        {
            ++(add_to[curr]);
            return true;
        }
        else
            add_to[curr] = '0';
    }

    return false;
}

std::pair<std::string, int32_t> addFloatPart(std::string & add, std::string & add_to)
{
    std::string resultFloat;

    int32_t reminderReturn{};

    // don't forget to reverse str
    if (add.size() != add_to.size())
    {
        // add remaining 0's
        if (add.size() < add_to.size())
        {
            while (add.size() != add_to.size())
            {
                auto tempBigger = add_to.back();
                add_to.pop_back();
                resultFloat.push_back(tempBigger);
            }
        }
        else
        {
            while (add.size() != add_to.size())
            {
                auto tempBigger = add.back();
                add.pop_back();
                resultFloat.push_back(tempBigger);
            }
        }
    }

    // now they are equal and have a form of 120(3921) 595

    for (int32_t i = add_to.size() - 1; i >= 0; --i)
    {
        int32_t add_toDigit = add_to[i] - '0';
        int32_t addDigit = add[i] - '0';

        if (add_toDigit + addDigit >= 10)
        {
            resultFloat.append(std::to_string((add_toDigit + addDigit) - 10));
            // we have a remainder
            if (i == 0 || !carryReminder(add_to, i - 1))
                reminderReturn = 1;
        }
        else
        {
            resultFloat.append(std::to_string(add_toDigit + addDigit));
        }
    }

    std::reverse(resultFloat.begin(), resultFloat.end());

    return std::make_pair(resultFloat, reminderReturn);

}


std::ostream & operator<<(std::ostream & os, const BigNumber & bn)
{
    os << bn.fullPart << "." << bn.floatPart;
    return os;
}

std::istream & operator>>(std::istream & is, BigNumber & bn)
{
    std::string temp;
    std::getline(is, temp, ' ');

    auto fullPartTemp = std::string(temp.begin(), std::find(temp.begin(), temp.end(), '.'));
    auto floatPartTemp = std::string(std::find(temp.begin(), temp.end(), '.') + 1, temp.end());

    bn.floatPart = floatPartTemp;
    bn.fullPart = fullPartTemp;

    return is;
}

void BigNumber::operator+=(BigNumber & bn)
{
    auto pair = addFloatPart(bn.floatPart, floatPart);

    floatPart = pair.first;

    if (pair.second > 0)
        addFullPart(std::to_string(std::stoi(bn.fullPart) + 1), fullPart);
    else
        addFullPart(bn.fullPart, fullPart);
}
Michael
  • 548
  • 6
  • 23

3 Answers3

3

I suggest that you first use getline to read a line. Then you can make an istringstream and use your >> on that. Specifically, you could add #include <sstream> and change the main function to the following:

int main()
{
    BigNumber bn, bntemp;

    std::string temp;
    std::getline(std::cin, temp);
    std::istringstream ln(temp);
    while (ln.good()) {
        ln >> bntemp;
        bn += bntemp;
    }

    std::cout << bn << std::endl;
    return 0;
}
Avery Musbach
  • 102
  • 1
  • 1
  • 7
  • 1
    This is how I would suggest doing it. However, you should replace `while (ln.good()) { ln >> bntemp; bn += bntemp; }` with `while (ln >> bntemp) { bn += bntemp; }` instead – Remy Lebeau Feb 15 '17 at 00:15
1

Two changes are needed. In main

Discarded the peek approach. Too brittle.

int main()
{
    BigNumber bn, bntemp;
    std::string line;
    std::getline(std::cin, line);
    std::stringstream stream(line);
    while (stream >> bntemp)
    {
        bn += bntemp;
    }

    std::cout << bn << std::endl;
    return 0;
}

And in operator>>

std::istream & operator >> (std::istream & is, BigNumber & bn)
{
    std::string temp;
    // also do NOTHING if the read fails!
    if (std::getline(is, temp, ' '))
    { 
        // recommend some isdigit testing in here to make sure you're not 
        // being fed garbage. Set fail flag in stream and bail out. 
        auto floatPartTemp = std::string(temp.begin(), std::find(temp.begin(), temp.end(), '.'));

        // if there is no . you are in for a world of hurt here
        auto floatPartTemp = std::string(std::find(temp.begin(), temp.end(), '.') + 1, temp.end());

        bn.floatPart = ;
        bn.fullPart = fullPartTemp;
    }
    return is;
}

So it should probably look more like

std::istream & operator >> (std::istream & is, BigNumber & bn)
{
    std::string temp;
    if (std::getline(is, temp, ' '))
    {
        if (std::all_of(temp.cbegin(), temp.cend(), [](char ch) { return isdigit(ch) || ch == '.'; }))
        {
            auto dotpos = std::find(temp.begin(), temp.end(), '.');
            bn.fullPart = std::string(temp.begin(), dotpos);
            std::string floatPartTemp;
            if (dotpos != temp.end())
            {
                floatPartTemp = std::string(dotpos + 1, temp.end());
            }
            bn.floatPart = floatPartTemp;
        }
        else
        {
            is.setstate(std::ios::failbit);
        }
    }
    return is;
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
-1

Perhaps you could then use

std::string temp;
is >> temp;

instead of std::getline().

If I remember well that breaks on spaces and keeps the newline in the buffer.

EmDroid
  • 5,918
  • 18
  • 18