Overflow checking in CFixed. Patch by JubJub (see #420).
This was SVN commit r7496.
This commit is contained in:
parent
d485ecafdc
commit
ab45b4c787
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user