You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
4.8 KiB
190 lines
4.8 KiB
/*-------------------------------------------------------------------------
|
|
* 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);
|
|
}
|