yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Jay KwakFix IEEE 754 NaN comparisons in constant folding (#7721)57567778b

master
6.9 KiB275 linesraw
1#ifndef SLANG_CORE_MATH_H
2#define SLANG_CORE_MATH_H
3
4#include "slang.h"
5
6#include <cmath>
7
8namespace Slang
9{
10// Some handy constants
11
12// The largest positive (or negative) number
13#define SLANG_HALF_MAX 65504.0f
14// Smallest (denormalized) value. 1 / 2^24
15#define SLANG_HALF_SUB_NORMAL_MIN (1.0f / 16777216.0f)
16
17class Math
18{
19public:
20    // Use to fix type punning issues with strict aliasing
21    union FloatIntUnion
22    {
23        float fvalue;
24        int ivalue;
25
26        SLANG_FORCE_INLINE static FloatIntUnion makeFromInt(int i)
27        {
28            FloatIntUnion cast;
29            cast.ivalue = i;
30            return cast;
31        }
32        SLANG_FORCE_INLINE static FloatIntUnion makeFromFloat(float f)
33        {
34            FloatIntUnion cast;
35            cast.fvalue = f;
36            return cast;
37        }
38    };
39    union DoubleInt64Union
40    {
41        double dvalue;
42        int64_t ivalue;
43        SLANG_FORCE_INLINE static DoubleInt64Union makeFromInt64(int64_t i)
44        {
45            DoubleInt64Union cast;
46            cast.ivalue = i;
47            return cast;
48        }
49        SLANG_FORCE_INLINE static DoubleInt64Union makeFromDouble(double d)
50        {
51            DoubleInt64Union cast;
52            cast.dvalue = d;
53            return cast;
54        }
55    };
56
57    static const float Pi;
58
59    template<typename T>
60    static T Abs(T a)
61    {
62        return (a < 0) ? -a : a;
63    }
64
65    template<typename T>
66    static T Min(const T& v1, const T& v2)
67    {
68        return v1 < v2 ? v1 : v2;
69    }
70    template<typename T>
71    static T Max(const T& v1, const T& v2)
72    {
73        return v1 > v2 ? v1 : v2;
74    }
75    template<typename T>
76    static T Min(const T& v1, const T& v2, const T& v3)
77    {
78        return Min(v1, Min(v2, v3));
79    }
80    template<typename T>
81    static T Max(const T& v1, const T& v2, const T& v3)
82    {
83        return Max(v1, Max(v2, v3));
84    }
85    template<typename T>
86    static T Clamp(const T& val, const T& vmin, const T& vmax)
87    {
88        if (val < vmin)
89            return vmin;
90        else if (val > vmax)
91            return vmax;
92        else
93            return val;
94    }
95
96    static inline int FastFloor(float x)
97    {
98        int i = (int)x;
99        return i - (i > x);
100    }
101
102    static inline int FastFloor(double x)
103    {
104        int i = (int)x;
105        return i - (i > x);
106    }
107
108    static inline int IsNaN(float x) { return std::isnan(x); }
109    static inline int IsNaN(double x) { return std::isnan(x); }
110
111    static inline int IsInf(float x) { return std::isinf(x); }
112    static inline int IsInf(double x) { return std::isinf(x); }
113
114    static inline unsigned int Ones32(unsigned int x)
115    {
116        /* 32-bit recursive reduction using SWAR...
117            but first step is mapping 2-bit values
118            into sum of 2 1-bit values in sneaky way
119        */
120        x -= ((x >> 1) & 0x55555555);
121        x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
122        x = (((x >> 4) + x) & 0x0f0f0f0f);
123        x += (x >> 8);
124        x += (x >> 16);
125        return (x & 0x0000003f);
126    }
127
128    static inline unsigned int Log2Floor(unsigned int x)
129    {
130        x |= (x >> 1);
131        x |= (x >> 2);
132        x |= (x >> 4);
133        x |= (x >> 8);
134        x |= (x >> 16);
135        return (Ones32(x >> 1));
136    }
137
138    static inline unsigned int Log2Ceil(unsigned int x)
139    {
140        int y = (x & (x - 1));
141        y |= -y;
142        y >>= (32 - 1);
143        x |= (x >> 1);
144        x |= (x >> 2);
145        x |= (x >> 4);
146        x |= (x >> 8);
147        x |= (x >> 16);
148        return (Ones32(x >> 1) - y);
149    }
150    /*
151    static inline int Log2(float x)
152    {
153        unsigned int ix = (unsigned int&)x;
154        unsigned int exp = (ix >> 23) & 0xFF;
155        int log2 = (unsigned int)(exp) - 127;
156
157        return log2;
158    }
159    */
160
161    static bool AreNearlyEqual(double a, double b, double epsilon)
162    {
163        // If they are equal then we are done
164        if (a == b)
165        {
166            return true;
167        }
168
169        const double absA = Abs(a);
170        const double absB = Abs(b);
171        const double diff = Abs(a - b);
172
173        // https://en.wikipedia.org/wiki/Double_precision_floating-point_format
174        const double minNormal = 2.2250738585072014e-308;
175        // Either a or b are very close to being zero, so doing relative comparison isn't really
176        // appropriate
177        if (a == 0.0 || b == 0.0 || (absA + absB < minNormal))
178        {
179            return diff < (epsilon * minNormal);
180        }
181        else
182        {
183            // Calculate a relative relative error
184            return diff < epsilon * (absA + absB);
185        }
186    }
187
188    template<typename T>
189    static T getLowestBit(T val)
190    {
191        return val & (-val);
192    }
193};
194inline int FloatAsInt(float val)
195{
196    return Math::FloatIntUnion::makeFromFloat(val).ivalue;
197}
198inline float IntAsFloat(int val)
199{
200    return Math::FloatIntUnion::makeFromInt(val).fvalue;
201}
202
203SLANG_FORCE_INLINE int64_t DoubleAsInt64(double val)
204{
205    return Math::DoubleInt64Union::makeFromDouble(val).ivalue;
206}
207SLANG_FORCE_INLINE double Int64AsDouble(int64_t value)
208{
209    return Math::DoubleInt64Union::makeFromInt64(value).dvalue;
210}
211
212inline unsigned short FloatToHalf(float val)
213{
214    const auto x = FloatAsInt(val);
215
216    unsigned short bits = (x >> 16) & 0x8000;
217    unsigned short m = (x >> 12) & 0x07ff;
218    unsigned int e = (x >> 23) & 0xff;
219    if (e < 103)
220        return bits;
221    if (e > 142)
222    {
223        bits |= 0x7c00u;
224        bits |= e == 255 && (x & 0x007fffffu);
225        return bits;
226    }
227    if (e < 113)
228    {
229        m |= 0x0800u;
230        bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
231        return bits;
232    }
233    bits |= ((e - 112) << 10) | (m >> 1);
234    bits += m & 1;
235    return bits;
236}
237
238inline float HalfToFloat(unsigned short input)
239{
240    static const auto magic = Math::FloatIntUnion::makeFromInt((127 + (127 - 15)) << 23);
241    static const auto was_infnan = Math::FloatIntUnion::makeFromInt((127 + 16) << 23);
242    Math::FloatIntUnion o;
243    o.ivalue = (input & 0x7fff) << 13; // exponent/mantissa bits
244    o.fvalue *= magic.fvalue;          // exponent adjust
245    if (o.fvalue >= was_infnan.fvalue) // make sure Inf/NaN survive
246        o.ivalue |= 255 << 23;
247    o.ivalue |= (input & 0x8000) << 16; // sign bit
248    return o.fvalue;
249}
250
251class Random
252{
253private:
254    unsigned int seed;
255
256public:
257    Random(int seed) { this->seed = seed; }
258    int Next() // random between 0 and RandMax (currently 0x7fff)
259    {
260        return ((seed = ((seed << 12) + 150889L) % 714025) & 0x7fff);
261    }
262    int Next(int min, int max) // inclusive min, exclusive max
263    {
264        unsigned int a = ((seed = ((seed << 12) + 150889L) % 714025) & 0xFFFF);
265        unsigned int b = ((seed = ((seed << 12) + 150889L) % 714025) & 0xFFFF);
266        unsigned int r = (a << 16) + b;
267        return min + r % (max - min);
268    }
269    float NextFloat() { return ((Next() << 15) + Next()) / ((float)(1 << 30)); }
270    float NextFloat(float valMin, float valMax) { return valMin + (valMax - valMin) * NextFloat(); }
271    static int RandMax() { return 0x7fff; }
272};
273} // namespace Slang
274
275#endif