1

I have what I believe to be a math bug come up that I do not fully understand why it's happening.

if (mNextBottomIndex < mBlocks.size() - 1) {
  if (mBlocks[mNextBottomIndex + 1]->getGlobalPosition().y >= -mBlocks[mNextBottomIndex + 1]->getHeight()) {
    mBlocks[mNextBottomIndex + 1]->setAlpha(1.0f);
    mNextBottomIndex++;
  }
}

it's crashing on the evaluation of the inner if statement because mBlocks.size() = 0 and mNextBottomIndex = 3 (or mNextBottomIndex equals any number > 0). So my question is, given the variables in the last sentence, how is it even getting past the outer if statement? The application is not handling these variables in a background thread, and the variables are the same before and after the outer if statement

I corrected the outer if statement to be

if (mBlocks.size() != 0 && mNextBottomIndex < mBlocks.size() - 1) {
  if (mBlocks[mNextBottomIndex + 1]->getGlobalPosition().y >= -mBlocks[mNextBottomIndex + 1]->getHeight()) {
    mBlocks[mNextBottomIndex + 1]->setAlpha(1.0f);
    mNextBottomIndex++;
  }
}

and it doesn't seem to be crashing now.

xosp7tom
  • 2,131
  • 1
  • 17
  • 26
Peter Chappy
  • 1,165
  • 4
  • 22
  • 41
  • 2
    if `mBlocks.size()` returns unsigned value, `mBlocks.size() - 1`, when `mBlocks.size()` returns `0`, _typically_ wraps around to a very large number (max number of its type). – Algirdas Preidžius Jan 24 '17 at 16:32
  • 2
    @AlgirdasPreidžius not "typically", *always*. Unsigned arithmetic is specified to wrap. – Quentin Jan 24 '17 at 16:33
  • @Quentin And this statement always confused me in light of the following quote: _If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined._ `-1` is not in the range of representable values of unsigned type, so it should, technically, be undefined. – Algirdas Preidžius Jan 24 '17 at 18:07
  • @AlgirdasPreidžius prior to being deemed not to be in the range of representable valus, I believe `mBlocks.size() - 1` is a prvalue (of an integer type), which means it can be implicitly _converted_ to any other integer type (by integer conversion). I believe this is a more specific context than the general context of expression evaluation. – dfrib Jan 24 '17 at 19:11
  • Another example where using most pedantic compiler switches would solve this problem. – Peter VARGA Jan 24 '17 at 20:03
  • @AlgirdasPreidžius does [this Q&A](http://stackoverflow.com/questions/18195715/why-is-unsigned-integer-overflow-defined-behavior-but-signed-integer-overflow-is) help? – Quentin Jan 24 '17 at 20:03
  • @Quentin Not really. Since all of the quotes, in the link that you pasted, stating that unsigned types cannot overflow are coming from the C standard, not C++. – Algirdas Preidžius Jan 24 '17 at 23:10
  • @dfri Interesting thought. All I could find is(4.5.1): _A prvalue of an integer type other than `bool`, `char16_t`, `char32_t`, or `wchar_t` whose integer conversion rank is less than the rank of `int` can be converted to a prvalue of type `int` if `int` can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int._ With (4.5.13): _The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type._ So I don't know if the condition of _being of lower conversion rank_ is satisfied in this case. – Algirdas Preidžius Jan 24 '17 at 23:28
  • @AlgirdasPreidžius Sec. 4.5 describe integral _promotions_, which I believe is not in play here. Integer conversion rank is described in Section 4.13 (see specifically Paragraph 1.4: _"The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type"); for equal-rank, we have no conversion. Thus, in the particular case of the question, I believe we are not dealing with integral promotion (Sec. 4.5) but rather, with integral _conversion_ (Sec. 4.7); as I see it, none of the paragraphs of Sec. 4.5 is applicable here, but rather paragraphs 1 and 2 of Sec. 4.7. – dfrib Jan 25 '17 at 08:08
  • @AlgirdasPreidžius C++14 final draft, §3.9.1.4: *"Unsigned integers shall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer. (48)"*. Note 48: *"This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type."* – Quentin Jan 25 '17 at 09:05
  • @Quentin Ahhh.. I see! I didn't even see those notes. Thank you for clarifying that! :) – Algirdas Preidžius Jan 25 '17 at 15:03
  • @dfri Well, I was reading _integral promotion_ as an implicit conversion, while _integral conversion_ as an explicit conversion (i.e. needing a cast to accomplish). – Algirdas Preidžius Jan 25 '17 at 15:05
  • @AlgirdasPreidžius I'm not sure I follow the last comment (if still being a question or just a statement :) ), but anyway (and possible redundant for you): integral conversions may also be implicit, and is by no means specific for explicit casting: this very Q&A shows such an case. Much of the content from Section 4.5 in the C++ standard is listed under section [Implicit Conversions at cppreference](http://en.cppreference.com/w/cpp/language/implicit_conversion) (see the sub-section Integral conversions). – dfrib Jan 25 '17 at 15:09

2 Answers2

9

mBlocks.size() is an unsigned int. if you minus 1 from an unsigned int of value 0 it will underflow into a large positive number.

london-deveoper
  • 533
  • 3
  • 8
2

You haven't shown us the type of mBlocks, but given that you are calling the size() member function of std::vector (or some similar stdlib container; e.g. std::array), the return type is of type size_type (from std::vector::size @ cppreference):

std::vector::size

size_type size() const;

Which is always an unsigned integer (from std::vector @ cppreference):

Member types

...

size_type Unsigned integer type (usually std::size_t)

When you subtract 1 from 0, you generate a negative number in an unsigned type (by integer conversion prior to performing the comparison), which (for this negative number -1 of small magnitude) will wrap around into a large unsigned integer, which will naturally pass the mNextBottomIndex < ... predicate as true.

See also e.g. the following:

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192