blob: c7617514076072d7af1f70b211d3894a6bbd4805 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Base Portability Library
* -------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Basic mathematical operations.
*//*--------------------------------------------------------------------*/
#include "deMath.h"
#include "deInt32.h"
#if (DE_COMPILER == DE_COMPILER_MSC)
# include <float.h>
#endif
#if (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
# include <fenv.h>
#endif
deRoundingMode deGetRoundingMode (void)
{
#if (DE_COMPILER == DE_COMPILER_MSC)
unsigned int status = 0;
int ret;
ret = _controlfp_s(&status, 0, 0);
DE_ASSERT(ret == 0);
switch (status & _MCW_RC)
{
case _RC_CHOP: return DE_ROUNDINGMODE_TO_ZERO;
case _RC_UP: return DE_ROUNDINGMODE_TO_POSITIVE_INF;
case _RC_DOWN: return DE_ROUNDINGMODE_TO_NEGATIVE_INF;
case _RC_NEAR: return DE_ROUNDINGMODE_TO_NEAREST_EVEN;
default: return DE_ROUNDINGMODE_LAST;
}
#elif (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
int mode = fegetround();
switch (mode)
{
case FE_TOWARDZERO: return DE_ROUNDINGMODE_TO_ZERO;
case FE_UPWARD: return DE_ROUNDINGMODE_TO_POSITIVE_INF;
case FE_DOWNWARD: return DE_ROUNDINGMODE_TO_NEGATIVE_INF;
case FE_TONEAREST: return DE_ROUNDINGMODE_TO_NEAREST_EVEN;
default: return DE_ROUNDINGMODE_LAST;
}
#else
# error Implement deGetRoundingMode().
#endif
}
deBool deSetRoundingMode (deRoundingMode mode)
{
#if (DE_COMPILER == DE_COMPILER_MSC)
unsigned int flag = 0;
unsigned int oldState;
int ret;
switch (mode)
{
case DE_ROUNDINGMODE_TO_ZERO: flag = _RC_CHOP; break;
case DE_ROUNDINGMODE_TO_POSITIVE_INF: flag = _RC_UP; break;
case DE_ROUNDINGMODE_TO_NEGATIVE_INF: flag = _RC_DOWN; break;
case DE_ROUNDINGMODE_TO_NEAREST_EVEN: flag = _RC_NEAR; break;
default:
DE_ASSERT(DE_FALSE);
}
ret = _controlfp_s(&oldState, flag, _MCW_RC);
return ret == 0;
#elif (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
int flag = 0;
int ret;
switch (mode)
{
case DE_ROUNDINGMODE_TO_ZERO: flag = FE_TOWARDZERO; break;
case DE_ROUNDINGMODE_TO_POSITIVE_INF: flag = FE_UPWARD; break;
case DE_ROUNDINGMODE_TO_NEGATIVE_INF: flag = FE_DOWNWARD; break;
case DE_ROUNDINGMODE_TO_NEAREST_EVEN: flag = FE_TONEAREST; break;
default:
DE_ASSERT(DE_FALSE);
}
ret = fesetround(flag);
return ret == 0;
#else
# error Implement deSetRoundingMode().
#endif
}
double deFractExp (double x, int* exponent)
{
if (deIsInf(x))
{
*exponent = 0;
return x;
}
else
{
int tmpExp = 0;
double fract = frexp(x, &tmpExp);
*exponent = tmpExp - 1;
return fract * 2.0;
}
}
/* We could use frexpf, if available. */
float deFloatFractExp (float x, int* exponent)
{
return (float)deFractExp(x, exponent);
}
double deRoundEven (double a)
{
double integer;
double fract = modf(a, &integer);
if (fabs(fract) == 0.5)
return 2.0 * deRound(a / 2.0);
return deRound(a);
}
float deInt32ToFloatRoundToNegInf (deInt32 x)
{
/* \note Sign bit is separate so the range is symmetric */
if (x >= -0xFFFFFF && x <= 0xFFFFFF)
{
/* 24 bits are representable (23 mantissa + 1 implicit). */
return (float)x;
}
else if (x != -0x7FFFFFFF - 1)
{
/* we are losing bits */
const int exponent = 31 - deClz32((deUint32)deAbs32(x));
const int numLostBits = exponent - 23;
const deUint32 lostMask = deBitMask32(0, numLostBits);
DE_ASSERT(numLostBits > 0);
if (x > 0)
{
/* Mask out lost bits to floor to a representable value */
return (float)(deInt32)(~lostMask & (deUint32)x);
}
else if ((lostMask & (deUint32)-x) == 0u)
{
/* this was a representable value */
DE_ASSERT( (deInt32)(float)x == x );
return (float)x;
}
else
{
/* not representable, choose the next lower */
const float nearestHigher = (float)-(deInt32)(~lostMask & (deUint32)-x);
const float oneUlp = (float)(1u << (deUint32)numLostBits);
const float nearestLower = nearestHigher - oneUlp;
/* check sanity */
DE_ASSERT((deInt32)(float)nearestHigher > (deInt32)(float)nearestLower);
return nearestLower;
}
}
else
return -(float)0x80000000u;
}
float deInt32ToFloatRoundToPosInf (deInt32 x)
{
if (x == -0x7FFFFFFF - 1)
return -(float)0x80000000u;
else
return -deInt32ToFloatRoundToNegInf(-x);
}