2
0
Эх сурвалжийг харах

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.
Fabian Giesen 4 жил өмнө
parent
commit
c38ec91d22
1 өөрчлөгдсөн 10 нэмэгдсэн , 8 устгасан
  1. 10 8
      stb_divide.h

+ 10 - 8
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