Home > Software engineering >  Non consistent if statment in C
Non consistent if statment in C

Time:04-21

So I have a simple code that behaves differently in 2 cases

In the first case, when I write it with (vecthread.size() - 5) inside the if statement it will go in the if statement and then throws an error "out_of_range". He goes in even tho the vector was empty.

vector<thread*> vecthread;
for (int i = 0; i < max; i  )
    {
        //int temp = vecthread.size() - 5;
        if((vecthread.size() - 5) > 0) {
           ...
        }
        thread* newThread = new thread(forj, max, mkg, x, &counter, &countermax);
        vecthread.push_back(newThread);
    }

But if i use the temp int here like so:

vector<thread*> vecthread;
for (int i = 0; i < max; i  )
    {
        int temp = vecthread.size() - 5;
        if(temp > 0) {
           ...
        }
        thread* newThread = new thread(forj, max, mkg, x, &counter, &countermax);
        vecthread.push_back(newThread);
    }

It will work correctly and not enter the if statement. Can anyone explain what's happening here? I don't wanna pollute my code with those unnessesary temp values if I can avoid it

Down below the full code:

void dspi() {
    double pi = 2 * acos(0.0);
    o.open("pi.dat");
    int max = 100;
    int max2 = pow(max,2);
    vector<int> lfg {1};
    vector<thread*> vecthread;
    calculatorlfg(max, &lfg); //calculates pseudorandom numbers using the lagged Fibonacci-Generator method and writes them into lfg
    int counter = 0;
    int countermax = 0;
    for (int i = 0; i < max; i  )
    {
        double x = betrag(lfg.at(i)/(pow(2,31)-1));
        int bla = vecthread.size() - 5;
        if((vecthread.size() - 5) > 0) {
            vecthread.at(vecthread.size()-5)->join();
            double temp = double(counter)/double(countermax);
            o << counter << "\t" << countermax<< "\t" << temp << "\t" << temp - pi/4 << endl;
            cout << countermax << "/" << max2 << endl;
        }
        thread* newThread = new thread(forj, max, mkg, x, &counter, &countermax);
        vecthread.push_back(newThread);
    }
    o.close();
}

CodePudding user response:

The cause of this difference is that vecthread.size() is not actually an int. It's an unsigned integral type, and there are some surprising interactions when you combine signed and unsigned integers in arithmetic expressions.

The simplest fix to your code is to change vecthread.size() - 5 > 0 to vecthread.size() > 5. But let's examine what exactly happens in each case.


First, let's check the type of vecthread.size(). According to cppreference it returns a size_type, which in turn is "unsigned integer type (usually std::size_t)" [Source].

Next, we look at what happens when we do vecthread.size() - 5. vecthread.size() is an unsigned size_type, 5 is a signed int, so C has to figure out how to reconcile this to perform the subtraction. We go now to the process of arithmetic conversions:

For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). ...

[Rules for what happens to enums and types smaller than int omitted]

Otherwise, the operand has integer type (because bool, char, char8_t, char16_t, char32_t, wchar_t, and unscoped enumeration were promoted at this point) and integral conversions are applied to produce the common type, as follows:

  • If both operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the operand with the greater integer conversion rank
  • Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.
  • Otherwise, if the signed operand's type can represent all values of the unsigned operand, the unsigned operand is converted to the signed operand's type
  • Otherwise, both operands are converted to the unsigned counterpart of the signed operand's type.

The first bullet point doesn't apply. The second is almost certainly what's happening in our case: size_type is almost certainly at least as high a rank as an int, and probably higher.

Therefore, vecthread.size() - 5 converts the 5 to a size_type and performs the subtraction between two unsigned integers. What this means is that when vecthread.size() is less than 5, the result of the subtraction is actually a very large positive number, not a small negative number. See here for more about this.

So vecthread.size() - 5 is some large unsigned size_type. We go through the whole process again to evaluate vecthread.size() - 5 > 0 that ends up promoting 0 to a size_type, and the result is true.


So why does storing it in int temp make a difference? When you perform that assignment, you take the large unsigned value and convert it to a signed int. So we check the rules for integral conversions to see what happens when we try to do this:

A prvalue of an integer type or of an unscoped enumeration type can be converted to any other integer type. If the conversion is listed under integral promotions, it is a promotion and not a conversion.

  • If the destination type is unsigned, the resulting value is the smallest unsigned value equal to the source value modulo 2^n, where n is the number of bits used to represent the destination type. That is, depending on whether the destination type is wider or narrower, signed integers are sign-extended or truncated and unsigned integers are zero-extended or truncated respectively.
  • If the destination type is signed, the value does not change if the source integer can be represented in the destination type. Otherwise the result is implementation-defined (until C 20)the unique value of the destination type equal to the source value modulo 2^n, where n is the number of bits used to represent the destination type. (since C 20). (Note that this is different from signed integer arithmetic overflow, which is undefined).

The destination type is int, which is signed. The value is too large to fit in an int, so prior to C 20 the value is implementation-defined, although most implementations would give the same results that C 20 now requires. This means that when you save the value to an int, you get the small negative value you were probably expecting it to be the entire time, meaning that the temp > 0 comparison works the way you think it does.

CodePudding user response:

As explained in the comments vector.size() returns an unsigned type value. Here is more about it: What is the difference between signed and unsigned int? Now if you prefer in this statement if((vecthread.size() - 5) > 0) to keep comparing it with 0 instead of 5 you can simply "cast" the left operand as int if((int) (vecthread.size() - 5) > 0) so as to get negative values and simply discard the temp variable.

  •  Tags:  
  • c
  • Related