DukhiAatma420's blog

By DukhiAatma420, history, 18 months ago, In English

Hi all,

I would like to share something which happened with me in today's contest. I solved the following problem: Strong Password. Here is my submission link: Submission.

I was not sure why it was giving runtime error, and after the contest, I tried submitting it in C++20. It passed without any problem! Here is the code snippet which was the reason of runtime error:

string s;
cin >> s;
...
 
vvll post(s.size(), vll(10, -1));
... 
for (ll i = s.size() - 2; i >= 0; --i) {
    for (int j = 0; j < 10; ++j)
        post[i][j] = post[i + 1][j];
    ...
}

vvll is vector<vector<long long>>, vll is vector<long long> and ll is long long.

Can you identify the source of runtime error here? Hint: what if input string is "0"?

Most of us assume size_t to be unsigned long long (as most of us compile our code using 64bit compilers locally). But this is not true from C++ library point of view.

Reason of failure of above code in C++ 17 (32 bit compiler):

  1. s.size() - 2 should semantically result in -1 and data type of size_t is unsigned integer of $$$4$$$ bytes.
    • This computation results in 0xFFFFFFFF ($$$4$$$ bytes, unsigned number).
  2. LHS is of data type signed long long, which is $$$8$$$ bytes.
    • LHS can safely handle consecutive $$$32$$$ bits which are all set to $$$1$$$.
    • Thus, $$$i$$$ will be set to 0x00000000 FFFFFFFF which is $$$4294967295$$$.

Reason of above code passing in C++ 17 (64 bit compiler):

  1. s.size() - 2 should semantically result in $$$-1$$$ and data type of size_t is unsigned integer of $$$8$$$ bytes.
    • This computation results in 0xFFFFFFFF FFFFFFFF ($$$8$$$ bytes, unsigned number).
  2. LHS is of data type signed long long, which is $$$8$$$ bytes.
    • LHS can't handle consecutive $$$64$$$ bits which are all set to $$$1$$$.
    • That value of $$$i$$$ is implementation defined and we can all agree that most of the implementations will simply treat this number as signed one, without any change in data.
    • Thus, $$$i$$$ will be set to 0xFFFFFFFF FFFFFFFF which is $$$-1$$$ and the code works as expected.

Possible workarounds?

  • We can change s.size() to (int)s.size() and the problem will be fixed. But this will mean that we need to put this everywhere where we are converting the result to long long in $$$32$$$ bit compilation.
  • We can use int i instead of long long i which will work.
  • We can use $$$64$$$ bit compilation to never bother about these nasty conversions :)
  • (Edit — New point) As mentioned in comments, there are possible cases of similar problems in 64 bit as well. We can use ssize in place of size() member function.

I am going with last point as it was a headache to figure out the problem during contest. And the worst part? I tried every possible combination of input in my local environment when the contest was live and there was nothing I could do to resolve this issue. Every possible combination was working as expected.

  • Vote: I like it
  • +7
  • Vote: I do not like it

| Write comment?
»
18 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by DukhiAatma420 (previous revision, new revision, compare).

»
18 months ago, # |
  Vote: I like it +9 Vote: I do not like it

use #define sz(a) (int)a.size()

»
18 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Use size(s) instead of s.size().

»
18 months ago, # |
  Vote: I like it +13 Vote: I do not like it

never bother about these nasty conversions :)

Are you sure? What do you think the output of this code is?

#include <string>
#include <iostream>

int main() {
    std::string s="a";

    for (int i = 0; i < s.size() - 2; i++) {
        std::cout << "hi" << std::endl;
        break;
    }
}

I'd recommend one of the options given in the comments (use ssize() or a macro to always convert to int).

  • »
    »
    18 months ago, # ^ |
    Rev. 2   Vote: I like it +3 Vote: I do not like it

    I am going to use ssize() from now on, as mentioned above.

    Also, thanks for giving me a counter example when we can have problems due to casting rules during comparison!

»
18 months ago, # |
  Vote: I like it +15 Vote: I do not like it

Those nasty conversions may even happen on 64 bit compilers. Check my blog. An issue encountered in Dy-tech cup contest with sqrt() fn. I solved during contest due to luck factor as mentioned.

I usually do (int)s.size() even on 64 bit compiler. And I think doing the right type castings would always be safer. In case of the blog above I needed use sqrtl() or type cast sqrt() arg into long double.

  • »
    »
    18 months ago, # ^ |
      Vote: I like it +3 Vote: I do not like it

    I agree with you in the blog points mentioned for the precision error, those create another set of problems for large numbers or cases designed specifically to fail in library usage.

    And problem here is more extreme than this. I was never told or saw anywhere that size_t can be 4 bytes. Sure, I was aware of type casting rules of using signed and unsigned carefully. But this was on another level when I tried testing my code for that exact same test case and it passed locally.

»
18 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by DukhiAatma420 (previous revision, new revision, compare).