povray/source/base/safemath.h
2013-11-06 13:07:19 -05:00

146 lines
6.1 KiB
C++

/*******************************************************************************
* safemath.h
*
* ---------------------------------------------------------------------------
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
*
* POV-Ray is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* POV-Ray is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------------
* POV-Ray is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
* ---------------------------------------------------------------------------
* $File: //depot/public/povray/3.x/source/base/safemath.h $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
#ifndef POVRAY_BASE_SAFEMATH_H
#define POVRAY_BASE_SAFEMATH_H
#include <cassert>
#include <limits>
#include "base/configbase.h"
#include "base/pov_err.h"
namespace pov_base
{
/// Multiply four (unsigned integer) factors, throwing an exception in case of numerical overflow.
template<typename T, typename T1, typename T2, typename T3, typename T4>
static inline T SafeUnsignedProduct(T1 p1, T2 p2, T3 p3, T4 p4)
{
// the function is intended for use with unisgned integer parameters only
// (NB: Instead of testing for (pN >= 0) we could also test for (!numeric_limits<TN>::is_signed),
// but this would make passing constant factors more cumbersome)
assert (numeric_limits<T>::is_integer);
assert (numeric_limits<T1>::is_integer && (p1 >= 0));
assert (numeric_limits<T2>::is_integer && (p2 >= 0));
assert (numeric_limits<T3>::is_integer && (p3 >= 0));
assert (numeric_limits<T4>::is_integer && (p4 >= 0));
// avoid divide-by-zero issues
if ((p1==0) || (p2==0) || (p3==0) || (p4==0))
return 0;
if ( (((numeric_limits<T>::max() / p4) / p3) / p2) < p1 )
throw POV_EXCEPTION_CODE(kNumericalLimitErr);
return T(p1) * T(p2) * T(p3) * T(p4);
}
/// Multiply three (unsigned integer) factors, throwing an exception in case of numerical overflow.
template<typename T, typename T1, typename T2, typename T3>
static inline T SafeUnsignedProduct(T1 p1, T2 p2, T3 p3)
{
return SafeUnsignedProduct<T,T1,T2,T3,unsigned int>(p1, p2, p3, 1u);
}
/// Multiply two (unsigned integer) factors, throwing an exception in case of numerical overflow.
template<typename T, typename T1, typename T2>
static inline T SafeUnsignedProduct(T1 p1, T2 p2)
{
return SafeUnsignedProduct<T,T1,T2,unsigned int,unsigned int>(p1, p2, 1u, 1u);
}
#if 0 // not currently used, but I hesitate to throw it away [CLi]
/// Multiply up to four (signed integer) values, throwing an exception in case of numerical overflow.
/// @note: The function will also throw an exception if negating the result would overflow.
template<typename T>
static inline T SafeSignedProduct(T p1, T p2, T p3 = 1, T p4 = 1)
{
// the function is intended for use with signed integer types only
assert (numeric_limits<T>::is_integer);
assert (numeric_limits<T>::is_signed);
// avoid divide-by-zero issues
if ((p1==0) || (p2==0) || (p3==0) || (p4==0))
return 0;
if (numeric_limits<T>::min() + numeric_limits<T>::max() == 0)
{
// integer representation appears to be sign-and-magnitude or one's complement; at any rate,
// abs(pN) is guaranteed to be a safe operation, and so is x/abs(pN) (as we've made sure that
// pN are all nonzero), and |::min()|==|::max()| is also guaranteed, i.e. the limits in the positive
// and negative domain are equally stringent.
if ( (((numeric_limits<T>::max() / abs(p4)) / abs(p3)) / abs(p2)) < abs(p1) )
throw POV_EXCEPTION_CODE(kNumericalLimitErr);
}
else if (numeric_limits<T>::min() + numeric_limits<T>::max() < 0)
{
// integer representation appears to be two's complement; at any rate, abs(pN) is a potentially
// unsafe operation, while -x is a safe operation for positive x; |::max()| > |::min()| is guaranteed,
// i.e. the limits in the positive domain are more stringent than those in the negative one.
// specifically handle situations in which abs(pN) would overflow
// NB we're deliberately not testing for pN == numeric_limits<T>::min(), in order to make the test robust
// against exotic integer representations
if ((p1 < -numeric_limits<T>::max()) ||
(p2 < -numeric_limits<T>::max()) ||
(p3 < -numeric_limits<T>::max()) ||
(p4 < -numeric_limits<T>::max()))
throw POV_EXCEPTION_CODE(kNumericalLimitErr);
// we've made sure that abs(pN) is a safe operation, and hence also x/abs(pN) (as we've also made sure that
// all pN are nonzero); we also know that whatever is safe in the positive domain is also safe in the
// negative domain
if ( (((numeric_limits<T>::max() / abs(p4)) / abs(p3)) / abs(p2)) < abs(p1) )
throw POV_EXCEPTION_CODE(kNumericalLimitErr);
}
else
{
// integer representation is exotic; abs(pN) is guaranteed to be a safe operation, and |::min()| > |::max()|
// is guaranteed, i.e. the limits in the negative domain are more stringent than those in the positive one.
// with abs(pN) a safe operation and having made sure all pN are non-zero, x/abs(pN) is guaranteed to be a safe
// operation as well; we also know that whatever is safe in the negative domain is also safe in the
// positive domain
if ( (((abs(numeric_limits<T>::min()) / abs(p4)) / abs(p3)) / abs(p2)) < abs(p1) )
throw POV_EXCEPTION_CODE(kNumericalLimitErr);
}
// product is safe, go ahead
return p1 * p2 * p3 * p4;
}
#endif
}
#endif // POVRAY_BASE_SAFEMATH_H