stb_divide: Fix integer overflow issues
We've established the signs of values before so we can carefully jiggle the expressions to be guaranteed overflow-free; the tests for <0 here were meant to check if the value was "still negative", i.e. if the sum did not underflow below INT_MIN, and we can rewrite that using algebra to be overflow-free. We need an extra case in the Euclidean dvision for INT_MIN / INT_MIN which is a bit annoying but trivial; finally, for two's complement platforms, note that abs(x) = (x > 0) ? x : -x does not work (since for x=INT_MIN, -x still gives INT_MIN), but the equivalent formulation for _negative_ absolute value (x < 0) ? x : -x is always in range and overflow-free, so rewrite the relevant expressions using that negative absolute value instead. Fixes issue #741.
This commit is contained in:
parent
dcb1116f13
commit
c38ec91d22
18
stb_divide.h
18
stb_divide.h
@ -166,15 +166,15 @@ int stb_div_floor(int v1, int v2)
|
||||
return v1/v2;
|
||||
#else
|
||||
if (v1 >= 0 && v2 < 0) {
|
||||
if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows
|
||||
return -stb__div(-v1+v2+1,v2); // nope, so just compute it
|
||||
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
|
||||
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
|
||||
else
|
||||
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
|
||||
}
|
||||
if (v1 < 0 && v2 >= 0) {
|
||||
if (v1 != INT_MIN) {
|
||||
if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows
|
||||
return -stb__div(v1-v2+1,-v2); // nope, so just compute it
|
||||
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
|
||||
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
|
||||
else
|
||||
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
|
||||
} else // it must be possible to compute -(v1+v2) without overflowing
|
||||
@ -209,8 +209,10 @@ int stb_div_eucl(int v1, int v2)
|
||||
else // if v1 is INT_MIN, we have to move away from overflow place
|
||||
if (v2 >= 0)
|
||||
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
|
||||
else
|
||||
else if (v2 != INT_MIN)
|
||||
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
|
||||
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
|
||||
q = 1, r = 0;
|
||||
#endif
|
||||
if (r >= 0)
|
||||
return q;
|
||||
@ -228,13 +230,13 @@ int stb_mod_trunc(int v1, int v2)
|
||||
if (r >= 0)
|
||||
return r;
|
||||
else
|
||||
return r + (v2 > 0 ? v2 : -v2);
|
||||
return r - (v2 < 0 ? v2 : -v2);
|
||||
} else { // modulus result should always be negative
|
||||
int r = stb__mod(v1,v2);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
else
|
||||
return r - (v2 > 0 ? v2 : -v2);
|
||||
return r + (v2 < 0 ? v2 : -v2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -267,7 +269,7 @@ int stb_mod_eucl(int v1, int v2)
|
||||
if (r >= 0)
|
||||
return r;
|
||||
else
|
||||
return r + (v2 > 0 ? v2 : -v2); // abs()
|
||||
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
|
||||
}
|
||||
|
||||
#ifdef STB_DIVIDE_TEST
|
||||
|
Loading…
Reference in New Issue
Block a user