1
0
forked from 0ad/0ad

Overflow checking in CFixed. Patch by JubJub (see #420).

This was SVN commit r7496.
This commit is contained in:
Ykkrosh 2010-05-02 20:16:53 +00:00
parent d485ecafdc
commit ab45b4c787
3 changed files with 153 additions and 20 deletions

View File

@ -21,6 +21,68 @@
#include "lib/types.h"
#include "maths/Sqrt.h"
#ifndef NDEBUG
#define USE_FIXED_OVERFLOW_CHECKS
#endif // NDEBUG
//define overflow macros
#ifndef USE_FIXED_OVERFLOW_CHECKS
#define CheckSignedSubtractionOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckSignedAdditionOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckCastOverflow(var, targetType, overflowWarning, underflowWarning)
#define CheckU32CastOverflow(var, targetType, overflowWarning)
#define CheckUnsignedAdditionOverflow(result, operand, overflowWarning)
#define CheckUnsignedSubtractionOverflow(result, operand, overflowWarning)
#define CheckNegationOverflow(var, type, overflowWarning)
#define CheckMultiplicationOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckDivisionOverflow(type, left, right, overflowWarning)
#else // USE_FIXED_OVERFLOW_CHECKS
#define CheckSignedSubtractionOverflow(type, left, right, overflowWarning, underflowWarning) \
if(left > 0 && right < 0 && left > std::numeric_limits<type>::max() + right) \
debug_warn(overflowWarning); \
else if(left < 0 && right > 0 && left < std::numeric_limits<type>::min() + right) \
debug_warn(underflowWarning);
#define CheckSignedAdditionOverflow(type, left, right, overflowWarning, underflowWarning) \
if(left > 0 && right > 0 && std::numeric_limits<type>::max() - left < right) \
debug_warn(overflowWarning); \
else if(left < 0 && right < 0 && std::numeric_limits<type>::min() - left > right) \
debug_warn(underflowWarning);
#define CheckCastOverflow(var, targetType, overflowWarning, underflowWarning) \
if(var > std::numeric_limits<targetType>::max()) \
debug_warn(overflowWarning); \
else if(var < std::numeric_limits<targetType>::min()) \
debug_warn(underflowWarning);
#define CheckU32CastOverflow(var, targetType, overflowWarning) \
if(var > (u32)std::numeric_limits<targetType>::max()) \
debug_warn(overflowWarning);
#define CheckUnsignedAdditionOverflow(result, operand, overflowWarning) \
if(result < operand) \
debug_warn(overflowWarning);
#define CheckUnsignedSubtractionOverflow(result, left, overflowWarning) \
if(result > left) \
debug_warn(overflowWarning);
#define CheckNegationOverflow(var, type, overflowWarning) \
if(value == std::numeric_limits<type>::min()) \
debug_warn(overflowWarning);
#define CheckMultiplicationOverflow(type, left, right, overflowWarning, underflowWarning) \
i64 res##left = (i64)left * (i64)right; \
CheckCastOverflow(res##left, type, overflowWarning, underflowWarning)
#define CheckDivisionOverflow(type, left, right, overflowWarning) \
if(right == -1) { CheckNegationOverflow(left, type, overflowWarning) }
#endif // USE_FIXED_OVERFLOW_CHECKS
template <typename T>
inline T round_away_from_zero(float value)
{
@ -115,32 +177,55 @@ public:
// Basic arithmetic:
/// Add a CFixed. Might overflow.
CFixed operator+(CFixed n) const { return CFixed(value + n.value); }
CFixed operator+(CFixed n) const
{
CheckSignedAdditionOverflow(T, value, n.value, L"Overflow in CFixed::operator+(CFixed n)", L"Underflow in CFixed::operator+(CFixed n)")
return CFixed(value + n.value);
}
/// Subtract a CFixed. Might overflow.
CFixed operator-(CFixed n) const { return CFixed(value - n.value); }
CFixed operator-(CFixed n) const
{
CheckSignedSubtractionOverflow(T, value, n.value, L"Overflow in CFixed::operator-(CFixed n)", L"Underflow in CFixed::operator-(CFixed n)")
return CFixed(value - n.value);
}
/// Add a CFixed. Might overflow.
CFixed& operator+=(CFixed n) { value += n.value; return *this; }
CFixed& operator+=(CFixed n) { *this = *this + n; return *this; }
/// Subtract a CFixed. Might overflow.
CFixed& operator-=(CFixed n) { value -= n.value; return *this; }
CFixed& operator-=(CFixed n) { *this = *this - n; return *this; }
/// Negate a CFixed.
CFixed operator-() const { return CFixed(-value); }
CFixed operator-() const
{
CheckNegationOverflow(value, T, L"Overflow in CFixed::operator-()")
return CFixed(-value);
}
/// Divide by a CFixed. Must not have n.IsZero(). Might overflow.
CFixed operator/(CFixed n) const
{
i64 t = (i64)value << fract_bits;
return CFixed((T)(t / (i64)n.value));
i64 result = t / (i64)n.value;
CheckCastOverflow(result, T, L"Overflow in CFixed::operator/(CFixed n)", L"Underflow in CFixed::operator/(CFixed n)")
return CFixed((T)result);
}
/// Multiply by an integer. Might overflow.
CFixed operator*(int n) const { return CFixed(value * n); }
CFixed operator*(int n) const
{
CheckMultiplicationOverflow(T, value, n, L"Overflow in CFixed::operator*(int n)", L"Underflow in CFixed::operator*(int n)")
return CFixed(value * n);
}
/// Divide by an integer. Must not have n == 0. Cannot overflow.
CFixed operator/(int n) const { return CFixed(value / n); }
CFixed operator/(int n) const
{
CheckDivisionOverflow(T, value, n, L"Overflow in CFixed::operator/(int n)")
return CFixed(value / n);
}
CFixed Absolute() const { return CFixed(abs(value)); }
@ -151,7 +236,10 @@ public:
CFixed Multiply(CFixed n) const
{
i64 t = (i64)value * (i64)n.value;
return CFixed((T)(t >> fract_bits));
t >>= fract_bits;
CheckCastOverflow(t, T, L"Overflow in CFixed::Multiply(CFixed n)", L"Underflow in CFixed::Multiply(CFixed n)")
return CFixed((T)t);
}
CFixed Sqrt() const

View File

@ -101,8 +101,14 @@ public:
// Do intermediate calculations with 64-bit ints to avoid overflows
i64 x = (i64)X.GetInternalValue();
i64 y = (i64)Y.GetInternalValue();
u64 d2 = (u64)(x * x + y * y);
u64 xx = (u64)(x * x);
u64 yy = (u64)(y * y);
u64 d2 = xx + yy;
CheckUnsignedAdditionOverflow(d2, xx, L"Overflow in CFixedVector2D::Length() part 1")
u32 d = isqrt64(d2);
CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector2D::Length() part 2")
fixed r;
r.SetInternalValue((i32)d);
return r;
@ -158,9 +164,13 @@ public:
{
i64 x = (i64)X.GetInternalValue() * (i64)v.X.GetInternalValue();
i64 y = (i64)Y.GetInternalValue() * (i64)v.Y.GetInternalValue();
CheckSignedAdditionOverflow(i64, x, y, L"Overflow in CFixedVector2D::Dot() part 1", L"Underflow in CFixedVector2D::Dot() part 1")
i64 sum = x + y;
sum >>= fixed::fract_bits;
CheckCastOverflow(sum, i32, L"Overflow in CFixedVector2D::Dot() part 2", L"Underflow in CFixedVector2D::Dot() part 2")
fixed ret;
ret.SetInternalValue((i32)(sum >> fixed::fract_bits));
ret.SetInternalValue((i32)sum);
return ret;
}

View File

@ -82,8 +82,18 @@ public:
i64 x = (i64)X.GetInternalValue();
i64 y = (i64)Y.GetInternalValue();
i64 z = (i64)Z.GetInternalValue();
u64 d2 = (u64)(x * x + y * y + z * z);
u64 xx = (u64)(x * x);
u64 yy = (u64)(y * y);
u64 zz = (u64)(z * z);
u64 t = xx + yy;
CheckUnsignedAdditionOverflow(t, xx, L"Overflow in CFixedVector3D::Length() part 1")
u64 d2 = t + zz;
CheckUnsignedAdditionOverflow(d2, t, L"Overflow in CFixedVector3D::Length() part 2")
u32 d = isqrt64(d2);
CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector3D::Length() part 3")
fixed r;
r.SetInternalValue((i32)d);
return r;
@ -134,13 +144,31 @@ public:
*/
CFixedVector3D Cross(const CFixedVector3D& v)
{
i64 x = ((i64)Y.GetInternalValue() * (i64)v.Z.GetInternalValue()) - ((i64)Z.GetInternalValue() * (i64)v.Y.GetInternalValue());
i64 y = ((i64)Z.GetInternalValue() * (i64)v.X.GetInternalValue()) - ((i64)X.GetInternalValue() * (i64)v.Z.GetInternalValue());
i64 z = ((i64)X.GetInternalValue() * (i64)v.Y.GetInternalValue()) - ((i64)Y.GetInternalValue() * (i64)v.X.GetInternalValue());
i64 y_vz = (i64)Y.GetInternalValue() * (i64)v.Z.GetInternalValue();
i64 z_vy = (i64)Z.GetInternalValue() * (i64)v.Y.GetInternalValue();
CheckSignedSubtractionOverflow(i64, y_vz, z_vy, L"Overflow in CFixedVector3D::Cross() part 1", L"Underflow in CFixedVector3D::Cross() part 1")
i64 x = y_vz - z_vy;
x >>= fixed::fract_bits;
i64 z_vx = (i64)Z.GetInternalValue() * (i64)v.X.GetInternalValue();
i64 x_vz = (i64)X.GetInternalValue() * (i64)v.Z.GetInternalValue();
CheckSignedSubtractionOverflow(i64, z_vx, x_vz, L"Overflow in CFixedVector3D::Cross() part 2", L"Underflow in CFixedVector3D::Cross() part 2")
i64 y = z_vx - x_vz;
y >>= fixed::fract_bits;
i64 x_vy = (i64)X.GetInternalValue() * (i64)v.Y.GetInternalValue();
i64 y_vx = (i64)Y.GetInternalValue() * (i64)v.X.GetInternalValue();
CheckSignedSubtractionOverflow(i64, x_vy, y_vx, L"Overflow in CFixedVector3D::Cross() part 3", L"Underflow in CFixedVector3D::Cross() part 3")
i64 z = x_vy - y_vx;
z >>= fixed::fract_bits;
CheckCastOverflow(x, i32, L"Overflow in CFixedVector3D::Cross() part 4", L"Underflow in CFixedVector3D::Cross() part 4")
CheckCastOverflow(y, i32, L"Overflow in CFixedVector3D::Cross() part 5", L"Underflow in CFixedVector3D::Cross() part 5")
CheckCastOverflow(z, i32, L"Overflow in CFixedVector3D::Cross() part 6", L"Underflow in CFixedVector3D::Cross() part 6")
CFixedVector3D ret;
ret.X.SetInternalValue((i32)(x >> fixed::fract_bits));
ret.Y.SetInternalValue((i32)(y >> fixed::fract_bits));
ret.Z.SetInternalValue((i32)(z >> fixed::fract_bits));
ret.X.SetInternalValue((i32)x);
ret.Y.SetInternalValue((i32)y);
ret.Z.SetInternalValue((i32)z);
return ret;
}
@ -152,9 +180,16 @@ public:
i64 x = (i64)X.GetInternalValue() * (i64)v.X.GetInternalValue();
i64 y = (i64)Y.GetInternalValue() * (i64)v.Y.GetInternalValue();
i64 z = (i64)Z.GetInternalValue() * (i64)v.Z.GetInternalValue();
i64 sum = x + y + z;
CheckSignedAdditionOverflow(i64, x, y, L"Overflow in CFixedVector3D::Dot() part 1", L"Underflow in CFixedVector3D::Dot() part 1")
i64 t = x + y;
CheckSignedAdditionOverflow(i64, t, z, L"Overflow in CFixedVector3D::Dot() part 2", L"Underflow in CFixedVector3D::Dot() part 2")
i64 sum = t + z;
sum >>= fixed::fract_bits;
CheckCastOverflow(sum, i32, L"Overflow in CFixedVector3D::Dot() part 3", L"Underflow in CFixedVector3D::Dot() part 3")
fixed ret;
ret.SetInternalValue((i32)(sum >> fixed::fract_bits));
ret.SetInternalValue((i32)sum);
return ret;
}
};