Add deInt32ToFloatRoundToNegInf and deInt32ToFloatRoundToPosInf.
[platform/upstream/VK-GL-CTS.git] / framework / delibs / debase / deMath.c
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
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
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
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. */
124 float deFloatFractExp (float x, int* exponent)
125 {
126         return (float)deFractExp(x, exponent);
127 }
128
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
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
183 float deInt32ToFloatRoundToPosInf (deInt32 x)
184 {
185         if (x == -0x7FFFFFFF - 1)
186                 return -(float)0x80000000u;
187         else
188                 return -deInt32ToFloatRoundToNegInf(-x);
189 }