简体   繁体   中英

Addition on user-defined types C++

I am writing my own class(called "Longer") such that it can hold a number without any upper bound unlike int. I am using std::string for this. I am having problem on performing addition.

  1. If i simply add two string, i can't get correct result.
  2. I thought of converting string to int and then performing addition, but long string can't be converted to int.

How can i define my own way of adding two strings so that i get the desired result? Here is the code:

Longer.h

#pragma once
#include <string>

class Longer
{
public:
   Longer(std::string number);
   Longer add(Longer num2);
   void print();
private:
   std::string number;
};

Longer.cpp

#include "Longer.h"
#include <iostream>
#include <string>

Longer::Longer(std::string num): number(num)
{
}

Longer Longer::add(Longer num2){
  return Longer(number+num2.number);
}

void Longer::print(){
std::cout<<number<<"\n";
}

main.cpp

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

int main(){

Longer num1("123456789101112");
Longer num2("121110987654321");


Longer num3 = num1.add(num2);
num3.print();

}

I don't wonder addition doesn't work like you intended. std::string is not meant to be used as an arbitrary-long number container, that's why.

You must define your own way to "add" two strings, which should consist into iterating backwards both strings (from the end) and compare single characters, by interpreting them as numbers.

without any upper bound unlike int

Be careful with such things. There will always be some upper bound with any solution, at the very least when your machine's memory is exhausted. A robust application should always have some kind of error checking.

If i simply add two string, i can't get correct result.

Well, that's obvious, isn't it? String concatentation doesn't know anything about mathematical semantics.

I thought of converting string to int and then performing addition, but long string can't be converted to int.

Exactly. Internally converting the string to a built-in type would defeat the whole purpose of the solution.

How can i define my own way of adding two strings so that i get the desired result?

The goal is apparently to support numbers bigger than what the built-in types provide.

First of all, are you really sure that your application needs to work with such huge numbers? Even a standard int should usually be more than enough, not to mention long long (standard since C++11 but practically usable even before that).

Perhaps what you really need is to detect invalid user input like "10000000000000000000000000" .

String streams provide this error detection for you. Here is a complete example for you to play with, including exemplary usage of std::numeric_limits :

#include <iostream>
#include <stdexcept>
#include <exception>
#include <limits>

int ReadInt()
{
    int result;
    std::cin >> result;
    if (!std::cin)
    {
        throw std::runtime_error("Illegal number");
    }
    return result;
}

int main()
{
    try
    {
        std::cout << "Enter number (max: " << std::numeric_limits<int>::max() << ") > ";
        int input = ReadInt();
        std::cout << "You entered the following number: " << input << "\n";
    }
    catch (std::exception const &exc)
    {
        std::cerr << exc.what() << "\n";
    }
}

Here are three example runs on my machine. The first with a "normal" small number, the second just barely larger than the maximum possible, the third exactly the largest possible integer:

Enter number (max: 2147483647) > 1000
You entered the following number: 1000

Enter number (max: 2147483647) > 2147483648
Illegal number

Enter number (max: 2147483647) > 2147483647
You entered the following number: 2147483647

Now, if you really really must support large integer numbers internally, don't reinvent the wheel. Use Boost.Multiprecision :

http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/index.html

Since the documentation of that particular library may be a bit hard to swallow, here is an ultra-simple example to get you started:

#include <iostream>
#include <stdexcept>
#include <exception>
#include <boost/multiprecision/cpp_int.hpp>

int main()
{
    try
    {
        boost::multiprecision::int128_t number("100000000000000000000000000000000");

        number *= 2;

        std::cout << number << "\n";
    }
    catch (std::exception const &exc)
    {
        std::cerr << exc.what() << "\n";
    }
}

This actually prints 200000000000000000000000000000000 .

#include <iostream>
using namespace std;

class Longer {
public:
    Longer(std::string number): number(number) {}
    void print() { cout << number << endl; }
    Longer add(Longer num2) {
        char over = '0'; string it;
        for(int i = number.size() - 1,
          j = num2.number.size() - 1;
          i >= 0 || j >= 0; i--, j--) {
            char one = i >= 0 ? number[i] : '0';
            char two = j >= 0 ? num2.number[j] : '0';
            char dig = one-'0' + two-'0' + over;
            over = '0'; if(dig > '9') {
                dig -= 10; over = '1'; }
            it.insert(0, 1, dig);
        }
        if(over != '0') it.insert(0, 1, over);
        return Longer(it);
    }
private:
   std::string number;
};

int main() {
    Longer num1("123456789101112"); num1.print();
    Longer num2("121110987654321"); num2.print();
    Longer num3 = num1.add(num2);   num3.print();
}

Output:

123456789101112
121110987654321
244567776755433

But if that was not homework, look at boost::multiprecision::cpp_int

Here is a ready to use solution

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <iterator>

class Longer
{
public:
    Longer() : value( 1, '0' ) {}

    Longer (std::string s ) 
        : value( s.rbegin(), s.rend() )
    {}

    Longer( const char *s ) 
        : value( std::reverse_iterator<const char *>( s + std::strlen( s ) ),
                 std::reverse_iterator<const char *>( s ) )
    {}             

    const Longer add( const Longer &number ) const;

    void print( std::ostream &os = std::cout ) const
    {
        os << std::string( value.rbegin(), value.rend() );
    }

private:
    std::string value;
};

const Longer Longer::add( const Longer &number ) const
{
    std::pair<std::string::size_type, std::string::size_type> sizes = 
        std::minmax( this->value.size(), number.value.size() );

    std::string result;
    result.reserve( sizes.second + 1 );

    int overflow = 0;

    auto out = std::transform( this->value.begin(), 
                               std::next( this->value.begin(), sizes.first ),
                               number.value.begin(), 
                               std::back_inserter( result ),
                               [&] ( char c1, char c2 ) ->char
                               {
                                   char c = ( c1 - '0' ) + ( c2 -'0' ) + overflow;
                                   overflow = c / 10;
                                   return c % 10 + '0';
                               } );

    std::string::const_iterator first, last;

    if ( this->value.size() < number.value.size() )
    {
        first = std::next( number.value.begin(), sizes.first );
        last  = number.value.end();
    }
    else
    {
        first = std::next( this->value.begin(), sizes.first );
        last  = this->value.end();
    }

    std::transform(first, last, out,
                   [&]( char c )
                   {
                       return ( c = c - '0' + overflow ), 
                              ( overflow = c / 10 ),
                              ( c % 10 + '0' );  
                   } );

    if ( overflow ) result.push_back( overflow + '0' );

    Longer n;
    n.value = result;

    return n;
}



int main() 
{
    Longer n1( "12345678912345678" );

    n1.print();
    std::cout << std::endl;

    Longer n2( "1123" );

    n2.print();
    std::cout << std::endl;

    Longer n3 = n2.add( "877" );

    n3.print();
    std::cout << std::endl;

    Longer n4( "9999999999" );

    n4.print();
    std::cout << std::endl;

    Longer n5 = n4.add( "1" );

    n5.print();
    std::cout << std::endl;

    return 0;
}

The output is

12345678912345678
1123
2000
9999999999
10000000000

Take into account that it is more convinient to store the string in the reverse order inside the class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM