Search code examples
c++static-castsize-type

why does subtraction overflow with static_cast?


I understand that s1.size() - s2.size() underflows when s2 is bigger because it's subtraction of unsigned. Why casting one them to int doesn't result in integer subtraction? Why casting the whole thing gives me the correct result? I expected it to evaluate what is inside parentheses, then underflow which would give a big number and then the cast to int would not make difference. What am I missing?

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

bool isShorter(const string &s1, const string &s2) {
    return (static_cast<int>(s1.size()) - s2.size() < 0) ? true : false; // underflows
    //return (static_cast<int>(s1.size() - s2.size()) < 0) ? true : false; // this works

}
int main() { 
    string s, t;
    getline(cin, s);
    getline(cin, t);
    cout << "s: " << s << endl;
    cout << "t: " << t << endl;
    cout << "printing shorter string of the two..." << endl;
    cout << ((isShorter(s, t)) ? s : t) << endl;
}

Solution

  • When you do

    static_cast<int>(s1.size()) - s2.size()
    

    You convert s1.size() to a int and then when you subtract s2.size() from it that int is promoted to the same type as s2.size() and then it is subtracted. This means you still have unsigned integer subtraction and since that can't ever be negative it will wrap around to a larger number. It is no different from doing s1.size() - s2.size().

    You have the same thing with

    static_cast<int>(s1.size() - s2.size())
    

    With the added bonus of possible signed integer overflow which is undefined behavior. You are still doing unsigned integer subtraction so if s1 is smaller than s2 than you wrap around to a large number.

    What you need to do is convert both s1.size() and s2.size() to a signed integer type to get singed integer subtraction. That could look like

    static_cast<ptrdiff_t>(s1.size())  - static_cast<ptrdiff_t>(s2.size())
    

    And now you will actually get a negative number if s1.size() is less than s2.size().


    It should be noted that all of this can be avoided by using less than operator. Your function can be rewritten to be

    bool isShorter(const string &s1, const string &s2)
    {
        return s1.size() < s2.size();
    }
    

    which, IMHO, is much easier to read and understand.