1 /*-------------------------------------------------------------------------
2 * drawElements Base Portability Library
3 * -------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Basic mathematical operations.
22 *//*--------------------------------------------------------------------*/
23
24 #include "deMath.h"
25 #include "deInt32.h"
26
27 #if (DE_COMPILER == DE_COMPILER_MSC)
28 # include <float.h>
29 #endif
30
31 #if (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
32 # include <fenv.h>
33 #endif
34
deGetRoundingMode(void)35 deRoundingMode deGetRoundingMode (void)
36 {
37 #if (DE_COMPILER == DE_COMPILER_MSC)
38 unsigned int status = 0;
39 int ret;
40
41 ret = _controlfp_s(&status, 0, 0);
42 DE_ASSERT(ret == 0);
43
44 switch (status & _MCW_RC)
45 {
46 case _RC_CHOP: return DE_ROUNDINGMODE_TO_ZERO;
47 case _RC_UP: return DE_ROUNDINGMODE_TO_POSITIVE_INF;
48 case _RC_DOWN: return DE_ROUNDINGMODE_TO_NEGATIVE_INF;
49 case _RC_NEAR: return DE_ROUNDINGMODE_TO_NEAREST;
50 default: return DE_ROUNDINGMODE_LAST;
51 }
52 #elif (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
53 int mode = fegetround();
54 switch (mode)
55 {
56 case FE_TOWARDZERO: return DE_ROUNDINGMODE_TO_ZERO;
57 case FE_UPWARD: return DE_ROUNDINGMODE_TO_POSITIVE_INF;
58 case FE_DOWNWARD: return DE_ROUNDINGMODE_TO_NEGATIVE_INF;
59 case FE_TONEAREST: return DE_ROUNDINGMODE_TO_NEAREST;
60 default: return DE_ROUNDINGMODE_LAST;
61 }
62 #else
63 # error Implement deGetRoundingMode().
64 #endif
65 }
66
deSetRoundingMode(deRoundingMode mode)67 deBool deSetRoundingMode (deRoundingMode mode)
68 {
69 #if (DE_COMPILER == DE_COMPILER_MSC)
70 unsigned int flag = 0;
71 unsigned int oldState;
72 int ret;
73
74 switch (mode)
75 {
76 case DE_ROUNDINGMODE_TO_ZERO: flag = _RC_CHOP; break;
77 case DE_ROUNDINGMODE_TO_POSITIVE_INF: flag = _RC_UP; break;
78 case DE_ROUNDINGMODE_TO_NEGATIVE_INF: flag = _RC_DOWN; break;
79 case DE_ROUNDINGMODE_TO_NEAREST: flag = _RC_NEAR; break;
80 default:
81 DE_ASSERT(DE_FALSE);
82 }
83
84 ret = _controlfp_s(&oldState, flag, _MCW_RC);
85 return ret == 0;
86 #elif (DE_COMPILER == DE_COMPILER_GCC) || (DE_COMPILER == DE_COMPILER_CLANG)
87 int flag = 0;
88 int ret;
89
90 switch (mode)
91 {
92 case DE_ROUNDINGMODE_TO_ZERO: flag = FE_TOWARDZERO; break;
93 case DE_ROUNDINGMODE_TO_POSITIVE_INF: flag = FE_UPWARD; break;
94 case DE_ROUNDINGMODE_TO_NEGATIVE_INF: flag = FE_DOWNWARD; break;
95 case DE_ROUNDINGMODE_TO_NEAREST: flag = FE_TONEAREST; break;
96 default:
97 DE_ASSERT(DE_FALSE);
98 }
99
100 ret = fesetround(flag);
101 return ret == 0;
102 #else
103 # error Implement deSetRoundingMode().
104 #endif
105 }
106
deFractExp(double x,int * exponent)107 double deFractExp (double x, int* exponent)
108 {
109 if (deIsInf(x))
110 {
111 *exponent = 0;
112 return x;
113 }
114 else
115 {
116 int tmpExp = 0;
117 double fract = frexp(x, &tmpExp);
118 *exponent = tmpExp - 1;
119 return fract * 2.0;
120 }
121 }
122
123 /* We could use frexpf, if available. */
deFloatFractExp(float x,int * exponent)124 float deFloatFractExp (float x, int* exponent)
125 {
126 return (float)deFractExp(x, exponent);
127 }
128
deRoundEven(double a)129 double deRoundEven (double a)
130 {
131 double integer;
132 double fract = modf(a, &integer);
133 if (fabs(fract) == 0.5)
134 return 2.0 * deRound(a / 2.0);
135 return deRound(a);
136 }
137
deInt32ToFloatRoundToNegInf(deInt32 x)138 float deInt32ToFloatRoundToNegInf (deInt32 x)
139 {
140 /* \note Sign bit is separate so the range is symmetric */
141 if (x >= -0xFFFFFF && x <= 0xFFFFFF)
142 {
143 /* 24 bits are representable (23 mantissa + 1 implicit). */
144 return (float)x;
145 }
146 else if (x != -0x7FFFFFFF - 1)
147 {
148 /* we are losing bits */
149 const int exponent = 31 - deClz32((deUint32)deAbs32(x));
150 const int numLostBits = exponent - 23;
151 const deUint32 lostMask = deBitMask32(0, numLostBits);
152
153 DE_ASSERT(numLostBits > 0);
154
155 if (x > 0)
156 {
157 /* Mask out lost bits to floor to a representable value */
158 return (float)(deInt32)(~lostMask & (deUint32)x);
159 }
160 else if ((lostMask & (deUint32)-x) == 0u)
161 {
162 /* this was a representable value */
163 DE_ASSERT( (deInt32)(float)x == x );
164 return (float)x;
165 }
166 else
167 {
168 /* not representable, choose the next lower */
169 const float nearestHigher = (float)-(deInt32)(~lostMask & (deUint32)-x);
170 const float oneUlp = (float)(1u << (deUint32)numLostBits);
171 const float nearestLower = nearestHigher - oneUlp;
172
173 /* check sanity */
174 DE_ASSERT((deInt32)(float)nearestHigher > (deInt32)(float)nearestLower);
175
176 return nearestLower;
177 }
178 }
179 else
180 return -(float)0x80000000u;
181 }
182
deInt32ToFloatRoundToPosInf(deInt32 x)183 float deInt32ToFloatRoundToPosInf (deInt32 x)
184 {
185 if (x == -0x7FFFFFFF - 1)
186 return -(float)0x80000000u;
187 else
188 return -deInt32ToFloatRoundToNegInf(-x);
189 }
190