/src/wasm3/source/m3_math_utils.h
Line | Count | Source |
1 | | // |
2 | | // m3_math_utils.h |
3 | | // |
4 | | // Created by Volodymyr Shymanksyy on 8/10/19. |
5 | | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. |
6 | | // |
7 | | |
8 | | #ifndef m3_math_utils_h |
9 | | #define m3_math_utils_h |
10 | | |
11 | | #include "m3_core.h" |
12 | | |
13 | | #include <limits.h> |
14 | | |
15 | | #if defined(M3_COMPILER_MSVC) |
16 | | |
17 | | # include <intrin.h> |
18 | | |
19 | | # define __builtin_popcount __popcnt |
20 | | |
21 | | static inline |
22 | | int __builtin_ctz (uint32_t x) |
23 | | { |
24 | | unsigned long ret; |
25 | | _BitScanForward(&ret, x); |
26 | | return (int)ret; |
27 | | } |
28 | | |
29 | | static inline |
30 | | int __builtin_clz (uint32_t x) |
31 | | { |
32 | | unsigned long ret; |
33 | | _BitScanReverse(&ret, x); |
34 | | return (int)(31 ^ ret); |
35 | | } |
36 | | |
37 | | |
38 | | # ifdef _WIN64 |
39 | | |
40 | | # define __builtin_popcountll __popcnt64 |
41 | | |
42 | | static inline |
43 | | int __builtin_ctzll (uint64_t value) |
44 | | { |
45 | | unsigned long ret; |
46 | | _BitScanForward64(&ret, value); |
47 | | return (int)ret; |
48 | | } |
49 | | |
50 | | static inline |
51 | | int __builtin_clzll (uint64_t value) |
52 | | { |
53 | | unsigned long ret; |
54 | | _BitScanReverse64(&ret, value); |
55 | | return (int)(63 ^ ret); |
56 | | } |
57 | | |
58 | | # else // _WIN64 |
59 | | |
60 | | # define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32)) |
61 | | |
62 | | static inline |
63 | | int __builtin_ctzll (uint64_t value) |
64 | | { |
65 | | //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway |
66 | | uint32_t msh = (uint32_t)(value >> 32); |
67 | | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); |
68 | | if (lsh != 0) { |
69 | | return __builtin_ctz(lsh); |
70 | | } |
71 | | return 32 + __builtin_ctz(msh); |
72 | | } |
73 | | |
74 | | static inline |
75 | | int __builtin_clzll (uint64_t value) |
76 | | { |
77 | | //if (value == 0) return 64; // Note: clz(0) result is undefined anyway |
78 | | uint32_t msh = (uint32_t)(value >> 32); |
79 | | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); |
80 | | if (msh != 0) { |
81 | | return __builtin_clz(msh); |
82 | | } |
83 | | return 32 + __builtin_clz(lsh); |
84 | | } |
85 | | |
86 | | # endif // _WIN64 |
87 | | |
88 | | #endif // defined(M3_COMPILER_MSVC) |
89 | | |
90 | | #if defined(M3_COMPILER_TCC) |
91 | | |
92 | | // TinyCC provides none of the bit-counting builtins the interpreter uses |
93 | | |
94 | | static inline |
95 | | int __builtin_popcount (uint32_t x) |
96 | | { |
97 | | x = x - ((x >> 1) & 0x55555555u); |
98 | | x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u); |
99 | | x = (x + (x >> 4)) & 0x0f0f0f0fu; |
100 | | return (int)((x * 0x01010101u) >> 24); |
101 | | } |
102 | | |
103 | | static inline |
104 | | int __builtin_ctz (uint32_t x) |
105 | | { |
106 | | //if (x == 0) return 32; // Note: ctz(0) result is undefined anyway |
107 | | return __builtin_popcount((x & (~x + 1u)) - 1); |
108 | | } |
109 | | |
110 | | static inline |
111 | | int __builtin_clz (uint32_t x) |
112 | | { |
113 | | //if (x == 0) return 32; // Note: clz(0) result is undefined anyway |
114 | | x |= x >> 1; |
115 | | x |= x >> 2; |
116 | | x |= x >> 4; |
117 | | x |= x >> 8; |
118 | | x |= x >> 16; |
119 | | return 32 - __builtin_popcount(x); |
120 | | } |
121 | | |
122 | | static inline |
123 | | int __builtin_popcountll (uint64_t value) |
124 | | { |
125 | | return __builtin_popcount((uint32_t)(value & 0xFFFFFFFF)) + |
126 | | __builtin_popcount((uint32_t)(value >> 32)); |
127 | | } |
128 | | |
129 | | static inline |
130 | | int __builtin_ctzll (uint64_t value) |
131 | | { |
132 | | uint32_t msh = (uint32_t)(value >> 32); |
133 | | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); |
134 | | if (lsh != 0) { |
135 | | return __builtin_ctz(lsh); |
136 | | } |
137 | | return 32 + __builtin_ctz(msh); |
138 | | } |
139 | | |
140 | | static inline |
141 | | int __builtin_clzll (uint64_t value) |
142 | | { |
143 | | uint32_t msh = (uint32_t)(value >> 32); |
144 | | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); |
145 | | if (msh != 0) { |
146 | | return __builtin_clz(msh); |
147 | | } |
148 | | return 32 + __builtin_clz(lsh); |
149 | | } |
150 | | |
151 | | |
152 | | // TinyCC compiles -x on floats as an arithmetic negation, which quiets signaling |
153 | | // NaNs; f32.neg/f64.neg must only flip the sign bit and leave the payload alone |
154 | | |
155 | | static inline |
156 | | f32 m3_negf (f32 x) |
157 | | { |
158 | | u32 bits; |
159 | | memcpy(&bits, &x, sizeof(bits)); |
160 | | bits ^= 0x80000000u; |
161 | | memcpy(&x, &bits, sizeof(x)); |
162 | | return x; |
163 | | } |
164 | | |
165 | | static inline |
166 | | f64 m3_neg (f64 x) |
167 | | { |
168 | | u64 bits; |
169 | | memcpy(&bits, &x, sizeof(bits)); |
170 | | bits ^= 0x8000000000000000ull; |
171 | | memcpy(&x, &bits, sizeof(x)); |
172 | | return x; |
173 | | } |
174 | | |
175 | | #endif // defined(M3_COMPILER_TCC) |
176 | | |
177 | | #if defined(__AVR__) |
178 | | |
179 | | static inline |
180 | | float rintf (float arg) |
181 | | { |
182 | | union { |
183 | | float f; |
184 | | uint32_t i; |
185 | | } u; |
186 | | u.f = arg; |
187 | | uint32_t ux = u.i & 0x7FFFFFFF; |
188 | | if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) { |
189 | | return arg; |
190 | | } |
191 | | return (float)lrint(arg); |
192 | | } |
193 | | |
194 | | static inline |
195 | | double rint (double arg) |
196 | | { |
197 | | union { |
198 | | double f; |
199 | | uint32_t i[2]; |
200 | | } u; |
201 | | u.f = arg; |
202 | | uint32_t ux = u.i[1] & 0x7FFFFFFF; |
203 | | if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) { |
204 | | return arg; |
205 | | } |
206 | | return (double)lrint(arg); |
207 | | } |
208 | | |
209 | | static inline |
210 | | uint64_t strtoull (const char* str, char** endptr, int base) |
211 | | { |
212 | | uint64_t result = 0; |
213 | | const char* p = str; |
214 | | |
215 | | while (*p == ' ' || *p == '\t') { |
216 | | p++; |
217 | | } |
218 | | |
219 | | if (base == 0) { |
220 | | if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) { |
221 | | base = 16; |
222 | | p += 2; |
223 | | } else if (*p == '0') { |
224 | | base = 8; |
225 | | p++; |
226 | | } else { |
227 | | base = 10; |
228 | | } |
229 | | } else if (base == 16 && *p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) { |
230 | | p += 2; |
231 | | } |
232 | | |
233 | | while (*p) { |
234 | | int digit; |
235 | | if (*p >= '0' && *p <= '9') { |
236 | | digit = *p - '0'; |
237 | | } else if (*p >= 'a' && *p <= 'f') { |
238 | | digit = *p - 'a' + 10; |
239 | | } else if (*p >= 'A' && *p <= 'F') { |
240 | | digit = *p - 'A' + 10; |
241 | | } else { |
242 | | break; |
243 | | } |
244 | | if (digit >= base) { |
245 | | break; |
246 | | } |
247 | | result = result * base + digit; |
248 | | p++; |
249 | | } |
250 | | |
251 | | if (endptr) { |
252 | | *endptr = (char*)p; |
253 | | } |
254 | | return result; |
255 | | } |
256 | | |
257 | | #endif |
258 | | |
259 | | /* |
260 | | * Rotr, Rotl |
261 | | */ |
262 | | |
263 | | static inline |
264 | | u32 rotl32 (u32 n, unsigned c) |
265 | 3.01M | { |
266 | 3.01M | const unsigned mask = CHAR_BIT * sizeof(n) - 1; |
267 | 3.01M | c &= mask & 31; |
268 | 3.01M | return (n << c) | (n >> ((32 - c) & mask)); |
269 | 3.01M | } |
270 | | |
271 | | static inline |
272 | | u32 rotr32 (u32 n, unsigned c) |
273 | 5.12M | { |
274 | 5.12M | const unsigned mask = CHAR_BIT * sizeof(n) - 1; |
275 | 5.12M | c &= mask & 31; |
276 | 5.12M | return (n >> c) | (n << ((32 - c) & mask)); |
277 | 5.12M | } |
278 | | |
279 | | static inline |
280 | | u64 rotl64 (u64 n, u64 c) |
281 | 3.23M | { |
282 | 3.23M | const unsigned mask = CHAR_BIT * sizeof(n) - 1; |
283 | 3.23M | c &= mask & 63; |
284 | 3.23M | return (n << c) | (n >> ((64 - c) & mask)); |
285 | 3.23M | } |
286 | | |
287 | | static inline |
288 | | u64 rotr64 (u64 n, u64 c) |
289 | 2.28M | { |
290 | 2.28M | const unsigned mask = CHAR_BIT * sizeof(n) - 1; |
291 | 2.28M | c &= mask & 63; |
292 | 2.28M | return (n >> c) | (n << ((64 - c) & mask)); |
293 | 2.28M | } |
294 | | |
295 | | /* |
296 | | * Wide multiply: the full 128-bit product of two 64-bit integers, as its low |
297 | | * half returned and its high half written through o_high |
298 | | */ |
299 | | |
300 | | static inline |
301 | | u64 mul_wide_u64 (u64 a, u64 b, u64* o_high) |
302 | 2.31M | { |
303 | 2.31M | #if defined(__SIZEOF_INT128__) && (UINTPTR_MAX > UINT32_MAX || defined(__wasm_wide_arithmetic__)) |
304 | | // a 64-bit target multiplies into a register pair in one instruction. A 32-bit |
305 | | // one that has the type anyway, wasm32 among them, would make a library call, |
306 | | // unless the target itself provides wide arithmetic instructions. |
307 | 2.31M | unsigned __int128 product = (unsigned __int128)a * b; |
308 | | |
309 | 2.31M | *o_high = (u64)(product >> 64); |
310 | 2.31M | return (u64)product; |
311 | | #else |
312 | | // four 32 x 32 partial products of the halves, a1:a0 times b1:b0. The middle |
313 | | // column collects the carry out of the low one and the low halves of the two |
314 | | // cross terms, at most 3 * (2^32 - 1). |
315 | | u64 a0 = (u32)a, a1 = a >> 32; |
316 | | u64 b0 = (u32)b, b1 = b >> 32; |
317 | | |
318 | | u64 ll = a0 * b0; |
319 | | u64 lh = a0 * b1; |
320 | | u64 hl = a1 * b0; |
321 | | u64 hh = a1 * b1; |
322 | | |
323 | | u64 middle = (ll >> 32) + (u32)lh + (u32)hl; |
324 | | |
325 | | *o_high = hh + (lh >> 32) + (hl >> 32) + (middle >> 32); |
326 | | return (middle << 32) | (u32)ll; |
327 | | #endif |
328 | 2.31M | } |
329 | | |
330 | | // A negative factor reads 2^64 too large as unsigned, which puts 2^64 times the |
331 | | // other factor too much into the product - one other factor too much in the high |
332 | | // half, which is where it is taken back out |
333 | | static inline |
334 | | u64 mul_wide_i64 (i64 a, i64 b, u64* o_high) |
335 | 1.67M | { |
336 | 1.67M | u64 low = mul_wide_u64((u64)a, (u64)b, o_high); |
337 | | |
338 | 1.67M | if (a < 0) { |
339 | 908k | *o_high -= (u64)b; |
340 | 908k | } |
341 | 1.67M | if (b < 0) { |
342 | 948k | *o_high -= (u64)a; |
343 | 948k | } |
344 | | |
345 | 1.67M | return low; |
346 | 1.67M | } |
347 | | |
348 | | /* |
349 | | * Integer Div, Rem |
350 | | */ |
351 | | |
352 | | #define OP_DIV_U(RES, A, B) \ |
353 | 80.9k | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ |
354 | 80.9k | RES = A / B; |
355 | | |
356 | | #define OP_REM_U(RES, A, B) \ |
357 | 87.7k | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ |
358 | 87.7k | RES = A % B; |
359 | | |
360 | | // 2's complement detection |
361 | | #if (INT_MIN != -INT_MAX) |
362 | | |
363 | | # define OP_DIV_S(RES, A, B, TYPE_MIN) \ |
364 | 108k | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ |
365 | 108k | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) { \ |
366 | 8 | newTrap (m3Err_trapIntegerOverflow); \ |
367 | 8 | } \ |
368 | 107k | RES = A / B; |
369 | | |
370 | | # define OP_REM_S(RES, A, B, TYPE_MIN) \ |
371 | 106k | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ |
372 | 106k | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ |
373 | 106k | else RES = A % B; |
374 | | |
375 | | #else |
376 | | |
377 | | # define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B) |
378 | | # define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B) |
379 | | |
380 | | #endif |
381 | | |
382 | | /* |
383 | | * Trunc |
384 | | */ |
385 | | |
386 | | #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ |
387 | 20.9M | if (M3_UNLIKELY(isnan(A))) { \ |
388 | 100 | newTrap (m3Err_trapIntegerConversion); \ |
389 | 100 | } \ |
390 | 20.9M | if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) { \ |
391 | 187 | newTrap (m3Err_trapIntegerOverflow); \ |
392 | 187 | } \ |
393 | 20.9M | RES = (TYPE)A; |
394 | | |
395 | | |
396 | 3.01M | #define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f) |
397 | 3.19M | #define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f) |
398 | 2.11M | #define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 ) |
399 | 2.40M | #define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 ) |
400 | | |
401 | 3.61M | #define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f) |
402 | 2.66M | #define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f) |
403 | 1.92M | #define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 ) |
404 | 2.00M | #define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 ) |
405 | | |
406 | | #define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \ |
407 | 45.3M | if (M3_UNLIKELY(isnan(A))) { \ |
408 | 9.37M | RES = 0; \ |
409 | 36.0M | } else if (M3_UNLIKELY(A <= RMIN)) { \ |
410 | 11.1M | RES = IMIN; \ |
411 | 24.8M | } else if (M3_UNLIKELY(A >= RMAX)) { \ |
412 | 10.6M | RES = IMAX; \ |
413 | 14.2M | } else { \ |
414 | 14.2M | RES = (TYPE)A; \ |
415 | 14.2M | } |
416 | | |
417 | 6.93M | #define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX) |
418 | 7.22M | #define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX) |
419 | 4.49M | #define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX) |
420 | 4.55M | #define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX) |
421 | | |
422 | 6.65M | #define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX) |
423 | 6.51M | #define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX) |
424 | 4.57M | #define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX) |
425 | 4.41M | #define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX) |
426 | | |
427 | | /* |
428 | | * Min, Max |
429 | | */ |
430 | | |
431 | | #if d_m3HasFloat |
432 | | |
433 | | # include <math.h> |
434 | | |
435 | | // Propagate a NaN operand the way the arithmetic ops do. |
436 | | // keep its sign and payload, but force the quiet bit, since the spec requires |
437 | | // min/max to produce an arithmetic NaN. |
438 | | |
439 | | static inline |
440 | | f32 quiet_nan_f32 (f32 arg) |
441 | 2.33M | { |
442 | 2.33M | union { |
443 | 2.33M | f32 f; |
444 | 2.33M | u32 i; |
445 | 2.33M | } u; |
446 | 2.33M | u.f = arg; |
447 | 2.33M | u.i |= 0x00400000; |
448 | 2.33M | return u.f; |
449 | 2.33M | } |
450 | | |
451 | | static inline |
452 | | f64 quiet_nan_f64 (f64 arg) |
453 | 860k | { |
454 | 860k | union { |
455 | 860k | f64 f; |
456 | 860k | u64 i; |
457 | 860k | } u; |
458 | 860k | u.f = arg; |
459 | 860k | u.i |= 0x0008000000000000ULL; |
460 | 860k | return u.f; |
461 | 860k | } |
462 | | |
463 | | static inline |
464 | | f32 min_f32 (f32 a, f32 b) |
465 | 3.80M | { |
466 | 3.80M | if (M3_UNLIKELY(isnan(a) or isnan(b))) { |
467 | 1.12M | return quiet_nan_f32(isnan(a) ? a : b); |
468 | 1.12M | } |
469 | 2.67M | if (M3_UNLIKELY(a == 0 and a == b)) { |
470 | 491k | return signbit(a) ? a : b; |
471 | 491k | } |
472 | 2.18M | return a > b ? b : a; |
473 | 2.67M | } |
474 | | |
475 | | static inline |
476 | | f32 max_f32 (f32 a, f32 b) |
477 | 3.99M | { |
478 | 3.99M | if (M3_UNLIKELY(isnan(a) or isnan(b))) { |
479 | 1.21M | return quiet_nan_f32(isnan(a) ? a : b); |
480 | 1.21M | } |
481 | 2.78M | if (M3_UNLIKELY(a == 0 and a == b)) { |
482 | 573k | return signbit(a) ? b : a; |
483 | 573k | } |
484 | 2.20M | return a > b ? a : b; |
485 | 2.78M | } |
486 | | |
487 | | static inline |
488 | | f64 min_f64 (f64 a, f64 b) |
489 | 1.72M | { |
490 | 1.72M | if (M3_UNLIKELY(isnan(a) or isnan(b))) { |
491 | 420k | return quiet_nan_f64(isnan(a) ? a : b); |
492 | 420k | } |
493 | 1.30M | if (M3_UNLIKELY(a == 0 and a == b)) { |
494 | 623k | return signbit(a) ? a : b; |
495 | 623k | } |
496 | 676k | return a > b ? b : a; |
497 | 1.30M | } |
498 | | |
499 | | static inline |
500 | | f64 max_f64 (f64 a, f64 b) |
501 | 1.64M | { |
502 | 1.64M | if (M3_UNLIKELY(isnan(a) or isnan(b))) { |
503 | 440k | return quiet_nan_f64(isnan(a) ? a : b); |
504 | 440k | } |
505 | 1.20M | if (M3_UNLIKELY(a == 0 and a == b)) { |
506 | 649k | return signbit(a) ? b : a; |
507 | 649k | } |
508 | 554k | return a > b ? a : b; |
509 | 1.20M | } |
510 | | |
511 | | // The NaN the spec calls canonical: quiet, positive, and with nothing in its |
512 | | // payload. Where an operation's result is a NaN, the spec allows any payload and any |
513 | | // sign, and the hardware picks - differently on different architectures. These pin |
514 | | // that down, so that a module's results are the same wherever it runs. |
515 | | // |
516 | | // Only operations the spec says produce an *arithmetic* NaN go through this. abs, |
517 | | // neg and copysign are defined bitwise and carry their operand's payload through by |
518 | | // design; canonicalizing them would be wrong, not merely stricter. |
519 | | # if d_m3CanonicalNaN |
520 | | |
521 | | static inline |
522 | | f32 canon_nan_f32 (f32 arg) |
523 | | { |
524 | | // arg != arg is the NaN test that does not need <math.h> to have been told to |
525 | | // keep it, and it is the whole of the cost when the value is not a NaN |
526 | | if (M3_UNLIKELY(arg != arg)) { |
527 | | union { |
528 | | f32 f; |
529 | | u32 i; |
530 | | } u; |
531 | | u.i = 0x7FC00000u; |
532 | | return u.f; |
533 | | } |
534 | | return arg; |
535 | | } |
536 | | |
537 | | static inline |
538 | | f64 canon_nan_f64 (f64 arg) |
539 | | { |
540 | | if (M3_UNLIKELY(arg != arg)) { |
541 | | union { |
542 | | f64 f; |
543 | | u64 i; |
544 | | } u; |
545 | | u.i = 0x7FF8000000000000ULL; |
546 | | return u.f; |
547 | | } |
548 | | return arg; |
549 | | } |
550 | | |
551 | | # else |
552 | | |
553 | 30.1M | # define canon_nan_f32(ARG) (ARG) |
554 | 20.5M | # define canon_nan_f64(ARG) (ARG) |
555 | | |
556 | | # endif // d_m3CanonicalNaN |
557 | | |
558 | | #endif |
559 | | |
560 | | #endif // m3_math_utils_h |