1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
|
// Do not include this header directly.
//
// Copyright 2020-2021 Binomial LLC
//
// 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.
// The general goal of these vectorized estimated math functions is scalability/performance.
// There are explictly no checks NaN's/Inf's on the input arguments. There are no assertions either.
// These are fast estimate functions - if you need more than that, use stdlib. Please do a proper
// engineering analysis before relying on them.
// I have chosen functions written by others, ported them to CppSPMD, then measured their abs/rel errors.
// I compared each to the ones in DirectXMath and stdlib's for accuracy/performance.
CPPSPMD_FORCE_INLINE vfloat fmod_inv(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
vfloat c = frac(abs(a * b_inv)) * abs(b);
return spmd_ternaryf(a < 0, -c, c);
}
CPPSPMD_FORCE_INLINE vfloat fmod_inv_p(const vfloat& a, const vfloat& b, const vfloat& b_inv)
{
return frac(a * b_inv) * b;
}
// Avoids dividing by zero or very small values.
CPPSPMD_FORCE_INLINE vfloat safe_div(vfloat a, vfloat b, float fDivThresh = 1e-7f)
{
return a / spmd_ternaryf( abs(b) > fDivThresh, b, spmd_ternaryf(b < 0.0f, -fDivThresh, fDivThresh) );
}
/*
clang 9.0.0 for win /fp:precise release
f range: 0.0000000000001250 10000000000.0000000000000000, vals: 1073741824
log2_est():
max abs err: 0.0000023076808731
max rel err: 0.0000000756678881
avg abs err: 0.0000007535452724
avg rel err: 0.0000000235117843
XMVectorLog2():
max abs err: 0.0000023329709933
max rel err: 0.0000000826961046
avg abs err: 0.0000007564889684
avg rel err: 0.0000000236051899
std::log2f():
max abs err: 0.0000020265979401
max rel err: 0.0000000626647654
avg abs err: 0.0000007494445227
avg rel err: 0.0000000233800985
*/
// See https://tech.ebayinc.com/engineering/fast-approximate-logarithms-part-iii-the-formulas/
inline vfloat spmd_kernel::log2_est(vfloat v)
{
vfloat signif, fexp;
// Just clamp to a very small value, instead of checking for invalid inputs.
vfloat x = max(v, 2.2e-38f);
/*
* Assume IEEE representation, which is sgn(1):exp(8):frac(23)
* representing (1+frac)*2^(exp-127). Call 1+frac the significand
*/
// get exponent
vint ux1_i = cast_vfloat_to_vint(x);
vint exp = VUINT_SHIFT_RIGHT(ux1_i & 0x7F800000, 23);
// actual exponent is exp-127, will subtract 127 later
vint ux2_i;
vfloat ux2_f;
vint greater = ux1_i & 0x00400000; // true if signif > 1.5
SPMD_SIF(greater != 0)
{
// signif >= 1.5 so need to divide by 2. Accomplish this by stuffing exp = 126 which corresponds to an exponent of -1
store_all(ux2_i, (ux1_i & 0x007FFFFF) | 0x3f000000);
store_all(ux2_f, cast_vint_to_vfloat(ux2_i));
// 126 instead of 127 compensates for division by 2
store_all(fexp, vfloat(exp - 126));
}
SPMD_SELSE(greater != 0)
{
// get signif by stuffing exp = 127 which corresponds to an exponent of 0
store(ux2_i, (ux1_i & 0x007FFFFF) | 0x3f800000);
store(ux2_f, cast_vint_to_vfloat(ux2_i));
store(fexp, vfloat(exp - 127));
}
SPMD_SENDIF
store_all(signif, ux2_f);
store_all(signif, signif - 1.0f);
const float a = 0.1501692f, b = 3.4226132f, c = 5.0225057f, d = 4.1130283f, e = 3.4813372f;
vfloat xm1 = signif;
vfloat xm1sqr = xm1 * xm1;
return fexp + ((a * (xm1sqr * xm1) + b * xm1sqr + c * xm1) / (xm1sqr + d * xm1 + e));
// fma lowers accuracy for SSE4.1 - no idea why (compiler reordering?)
//return fexp + ((vfma(a, (xm1sqr * xm1), vfma(b, xm1sqr, c * xm1))) / (xm1sqr + vfma(d, xm1, e)));
}
// Uses log2_est(), so this function must be <= the precision of that.
inline vfloat spmd_kernel::log_est(vfloat v)
{
return log2_est(v) * 0.693147181f;
}
CPPSPMD_FORCE_INLINE void spmd_kernel::reduce_expb(vfloat& arg, vfloat& two_int_a, vint& adjustment)
{
// Assume we're using equation (2)
store_all(adjustment, 0);
// integer part of the input argument
vint int_arg = (vint)arg;
// if frac(arg) is in [0.5, 1.0]...
SPMD_SIF((arg - int_arg) > 0.5f)
{
store(adjustment, 1);
// then change it to [0.0, 0.5]
store(arg, arg - 0.5f);
}
SPMD_SENDIF
// arg == just the fractional part
store_all(arg, arg - (vfloat)int_arg);
// Now compute 2** (int) arg.
store_all(int_arg, min(int_arg + 127, 254));
store_all(two_int_a, cast_vint_to_vfloat(VINT_SHIFT_LEFT(int_arg, 23)));
}
/*
clang 9.0.0 for win /fp:precise release
f range : -50.0000000000000000 49.9999940395355225, vals : 16777216
exp2_est():
Total passed near - zero check : 16777216
Total sign diffs : 0
max abs err: 1668910609.7500000000000000
max rel err: 0.0000015642030031
avg abs err: 10793794.4007573910057545
avg rel err: 0.0000003890893282
XMVectorExp2():
Total passed near-zero check: 16777216
Total sign diffs: 0
max abs err: 1665552836.8750000000000000
max rel err: 0.0000114674862370
avg abs err: 10771868.2627860084176064
avg rel err: 0.0000011218880770
std::exp2f():
Total passed near-zero check: 16777216
Total sign diffs: 0
max abs err: 1591636585.6250000000000000
max rel err: 0.0000014849731018
avg abs err: 10775800.3204844966530800
avg rel err: 0.0000003851496422
*/
// http://www.ganssle.com/item/approximations-c-code-exponentiation-log.htm
inline vfloat spmd_kernel::exp2_est(vfloat arg)
{
SPMD_BEGIN_CALL
const vfloat P00 = +7.2152891521493f;
const vfloat P01 = +0.0576900723731f;
const vfloat Q00 = +20.8189237930062f;
const vfloat Q01 = +1.0f;
const vfloat sqrt2 = 1.4142135623730950488f; // sqrt(2) for scaling
vfloat result = 0.0f;
// Return 0 if arg is too large.
// We're not introducing inf/nan's into calculations, or risk doing so by returning huge default values.
SPMD_IF(abs(arg) > 126.0f)
{
spmd_return();
}
SPMD_END_IF
// 2**(int(a))
vfloat two_int_a;
// set to 1 by reduce_expb
vint adjustment;
// 0 if arg is +; 1 if negative
vint negative = 0;
// If the input is negative, invert it. At the end we'll take the reciprocal, since n**(-1) = 1/(n**x).
SPMD_SIF(arg < 0.0f)
{
store(arg, -arg);
store(negative, 1);
}
SPMD_SENDIF
store_all(arg, min(arg, 126.0f));
// reduce to [0.0, 0.5]
reduce_expb(arg, two_int_a, adjustment);
// The format of the polynomial is:
// answer=(Q(x**2) + x*P(x**2))/(Q(x**2) - x*P(x**2))
//
// The following computes the polynomial in several steps:
// Q(x**2)
vfloat Q = vfma(Q01, (arg * arg), Q00);
// x*P(x**2)
vfloat x_P = arg * (vfma(P01, arg * arg, P00));
vfloat answer = (Q + x_P) / (Q - x_P);
// Now correct for the scaling factor of 2**(int(a))
store_all(answer, answer * two_int_a);
// If the result had a fractional part > 0.5, correct for that
store_all(answer, spmd_ternaryf(adjustment != 0, answer * sqrt2, answer));
// Correct for a negative input
SPMD_SIF(negative != 0)
{
store(answer, 1.0f / answer);
}
SPMD_SENDIF
store(result, answer);
return result;
}
inline vfloat spmd_kernel::exp_est(vfloat arg)
{
// e^x = exp2(x / log_base_e(2))
// constant is 1.0/(log(2)/log(e)) or 1/log(2)
return exp2_est(arg * 1.44269504f);
}
inline vfloat spmd_kernel::pow_est(vfloat arg1, vfloat arg2)
{
return exp_est(log_est(arg1) * arg2);
}
/*
clang 9.0.0 for win /fp:precise release
Total near-zero: 144, output above near-zero tresh: 30
Total near-zero avg: 0.0000067941016621 max: 0.0000134706497192
Total near-zero sign diffs: 5
Total passed near-zero check: 16777072
Total sign diffs: 5
max abs err: 0.0000031375306036
max rel err: 0.1140846017075028
avg abs err: 0.0000003026226621
avg rel err: 0.0000033564977623
*/
// Math from this web page: http://developer.download.nvidia.com/cg/sin.html
// This is ~2x slower than sin_est() or cos_est(), and less accurate, but I'm keeping it here for comparison purposes to help validate/sanity check sin_est() and cos_est().
inline vfloat spmd_kernel::sincos_est_a(vfloat a, bool sin_flag)
{
const float c0_x = 0.0f, c0_y = 0.5f, c0_z = 1.0f;
const float c1_x = 0.25f, c1_y = -9.0f, c1_z = 0.75f, c1_w = 0.159154943091f;
const float c2_x = 24.9808039603f, c2_y = -24.9808039603f, c2_z = -60.1458091736f, c2_w = 60.1458091736f;
const float c3_x = 85.4537887573f, c3_y = -85.4537887573f, c3_z = -64.9393539429f, c3_w = 64.9393539429f;
const float c4_x = 19.7392082214f, c4_y = -19.7392082214f, c4_z = -1.0f, c4_w = 1.0f;
vfloat r0_x, r0_y, r0_z, r1_x, r1_y, r1_z, r2_x, r2_y, r2_z;
store_all(r1_x, sin_flag ? vfms(c1_w, a, c1_x) : c1_w * a);
store_all(r1_y, frac(r1_x));
store_all(r2_x, (vfloat)(r1_y < c1_x));
store_all(r2_y, (vfloat)(r1_y >= c1_y));
store_all(r2_z, (vfloat)(r1_y >= c1_z));
store_all(r2_y, vfma(r2_x, c4_z, vfma(r2_y, c4_w, r2_z * c4_z)));
store_all(r0_x, c0_x - r1_y);
store_all(r0_y, c0_y - r1_y);
store_all(r0_z, c0_z - r1_y);
store_all(r0_x, r0_x * r0_x);
store_all(r0_y, r0_y * r0_y);
store_all(r0_z, r0_z * r0_z);
store_all(r1_x, vfma(c2_x, r0_x, c2_z));
store_all(r1_y, vfma(c2_y, r0_y, c2_w));
store_all(r1_z, vfma(c2_x, r0_z, c2_z));
store_all(r1_x, vfma(r1_x, r0_x, c3_x));
store_all(r1_y, vfma(r1_y, r0_y, c3_y));
store_all(r1_z, vfma(r1_z, r0_z, c3_x));
store_all(r1_x, vfma(r1_x, r0_x, c3_z));
store_all(r1_y, vfma(r1_y, r0_y, c3_w));
store_all(r1_z, vfma(r1_z, r0_z, c3_z));
store_all(r1_x, vfma(r1_x, r0_x, c4_x));
store_all(r1_y, vfma(r1_y, r0_y, c4_y));
store_all(r1_z, vfma(r1_z, r0_z, c4_x));
store_all(r1_x, vfma(r1_x, r0_x, c4_z));
store_all(r1_y, vfma(r1_y, r0_y, c4_w));
store_all(r1_z, vfma(r1_z, r0_z, c4_z));
store_all(r0_x, vfnma(r1_x, r2_x, vfnma(r1_y, r2_y, r1_z * -r2_z)));
return r0_x;
}
// positive values only
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::recip_est1(const vfloat& q)
{
//const int mag = 0x7EF312AC; // 2 NR iters, 3 is 0x7EEEEBB3
const int mag = 0x7EF311C3;
const float fMinThresh = .0000125f;
vfloat l = spmd_ternaryf(q >= fMinThresh, q, cast_vint_to_vfloat(vint(mag)));
vint x_l = vint(mag) - cast_vfloat_to_vint(l);
vfloat rcp_l = cast_vint_to_vfloat(x_l);
return rcp_l * vfnma(rcp_l, q, 2.0f);
}
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::recip_est1_pn(const vfloat& t)
{
//const int mag = 0x7EF312AC; // 2 NR iters, 3 is 0x7EEEEBB3
const int mag = 0x7EF311C3;
const float fMinThresh = .0000125f;
vfloat s = sign(t);
vfloat q = abs(t);
vfloat l = spmd_ternaryf(q >= fMinThresh, q, cast_vint_to_vfloat(vint(mag)));
vint x_l = vint(mag) - cast_vfloat_to_vint(l);
vfloat rcp_l = cast_vint_to_vfloat(x_l);
return rcp_l * vfnma(rcp_l, q, 2.0f) * s;
}
// https://basesandframes.files.wordpress.com/2020/04/even_faster_math_functions_green_2020.pdf
// https://github.com/hcs0/Hackers-Delight/blob/master/rsqrt.c.txt
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::rsqrt_est1(vfloat x0)
{
vfloat xhalf = 0.5f * x0;
vfloat x = cast_vint_to_vfloat(vint(0x5F375A82) - (VINT_SHIFT_RIGHT(cast_vfloat_to_vint(x0), 1)));
return x * vfnma(xhalf * x, x, 1.5008909f);
}
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::rsqrt_est2(vfloat x0)
{
vfloat xhalf = 0.5f * x0;
vfloat x = cast_vint_to_vfloat(vint(0x5F37599E) - (VINT_SHIFT_RIGHT(cast_vfloat_to_vint(x0), 1)));
vfloat x1 = x * vfnma(xhalf * x, x, 1.5);
vfloat x2 = x1 * vfnma(xhalf * x1, x1, 1.5);
return x2;
}
// Math from: http://developer.download.nvidia.com/cg/atan2.html
// TODO: Needs more validation, parameter checking.
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::atan2_est(vfloat y, vfloat x)
{
vfloat t1 = abs(y);
vfloat t3 = abs(x);
vfloat t0 = max(t3, t1);
store_all(t1, min(t3, t1));
store_all(t3, t1 / t0);
vfloat t4 = t3 * t3;
store_all(t0, vfma(-0.013480470f, t4, 0.057477314f));
store_all(t0, vfms(t0, t4, 0.121239071f));
store_all(t0, vfma(t0, t4, 0.195635925f));
store_all(t0, vfms(t0, t4, 0.332994597f));
store_all(t0, vfma(t0, t4, 0.999995630f));
store_all(t3, t0 * t3);
store_all(t3, spmd_ternaryf(abs(y) > abs(x), vfloat(1.570796327f) - t3, t3));
store_all(t3, spmd_ternaryf(x < 0.0f, vfloat(3.141592654f) - t3, t3));
store_all(t3, spmd_ternaryf(y < 0.0f, -t3, t3));
return t3;
}
/*
clang 9.0.0 for win /fp:precise release
Tested range: -25.1327412287183449 25.1327382326621169, vals : 16777216
Skipped angles near 90/270 within +- .001 radians.
Near-zero threshold: .0000125f
Near-zero output above check threshold: 1e-6f
Total near-zero: 144, output above near-zero tresh: 20
Total near-zero avg: 0.0000067510751968 max: 0.0000133514404297
Total near-zero sign diffs: 5
Total passed near-zero check: 16766400
Total sign diffs: 5
max abs err: 1.4982600811139264
max rel err: 0.1459155900188041
avg rel err: 0.0000054659502568
XMVectorTan() precise:
Total near-zero: 144, output above near-zero tresh: 18
Total near-zero avg: 0.0000067641216186 max: 0.0000133524126795
Total near-zero sign diffs: 0
Total passed near-zero check: 16766400
Total sign diffs: 0
max abs err: 1.9883573246424930
max rel err: 0.1459724171926864
avg rel err: 0.0000054965766843
std::tanf():
Total near-zero: 144, output above near-zero tresh: 0
Total near-zero avg: 0.0000067116930779 max: 0.0000127713074107
Total near-zero sign diffs: 11
Total passed near-zero check: 16766400
Total sign diffs: 11
max abs err: 0.8989131818294709
max rel err: 0.0573181403173166
avg rel err: 0.0000030791301203
Originally from:
http://www.ganssle.com/approx.htm
*/
CPPSPMD_FORCE_INLINE vfloat spmd_kernel::tan82(vfloat x)
{
// Original double version was 8.2 digits
//double c1 = 211.849369664121f, c2 = -12.5288887278448f, c3 = 269.7350131214121f, c4 = -71.4145309347748f;
// Tuned float constants for lower avg rel error (without using FMA3):
const float c1 = 211.849350f, c2 = -12.5288887f, c3 = 269.734985f, c4 = -71.4145203f;
vfloat x2 = x * x;
return (x * (vfma(c2, x2, c1)) / (vfma(x2, (c4 + x2), c3)));
}
// Don't call this for angles close to 90/270!.
inline vfloat spmd_kernel::tan_est(vfloat x)
{
const float fPi = 3.141592653589793f, fOneOverPi = 0.3183098861837907f;
CPPSPMD_DECL(const uint8_t, s_table0[16]) = { 128 + 0, 128 + 2, 128 + -2, 128 + 4, 128 + 0, 128 + 2, 128 + -2, 128 + 4, 128 + 0, 128 + 2, 128 + -2, 128 + 4, 128 + 0, 128 + 2, 128 + -2, 128 + 4 };
vint table = init_lookup4(s_table0); // a load
vint sgn = cast_vfloat_to_vint(x) & 0x80000000;
store_all(x, abs(x));
vfloat orig_x = x;
vfloat q = x * fOneOverPi;
store_all(x, q - floor(q));
vfloat x4 = x * 4.0f;
vint octant = (vint)(x4);
vfloat x0 = spmd_ternaryf((octant & 1) != 0, -x4, x4);
vint k = table_lookup4_8(octant, table) & 0xFF; // a shuffle
vfloat bias = (vfloat)k + -128.0f;
vfloat y = x0 + bias;
vfloat z = tan82(y);
vfloat r;
vbool octant_one_or_two = (octant == 1) || (octant == 2);
// SPMD optimization - skip costly divide if we can
if (spmd_any(octant_one_or_two))
{
const float fDivThresh = .4371e-7f;
vfloat one_over_z = 1.0f / spmd_ternaryf(abs(z) > fDivThresh, z, spmd_ternaryf(z < 0.0f, -fDivThresh, fDivThresh));
vfloat b = spmd_ternaryf(octant_one_or_two, one_over_z, z);
store_all(r, spmd_ternaryf((octant & 2) != 0, -b, b));
}
else
{
store_all(r, spmd_ternaryf(octant == 0, z, -z));
}
// Small angle approximation, to decrease the max rel error near Pi.
SPMD_SIF(x >= (1.0f - .0003125f*4.0f))
{
store(r, vfnma(floor(q) + 1.0f, fPi, orig_x));
}
SPMD_SENDIF
return cast_vint_to_vfloat(cast_vfloat_to_vint(r) ^ sgn);
}
inline void spmd_kernel::seed_rand(rand_context& x, vint seed)
{
store(x.a, 0xf1ea5eed);
store(x.b, seed ^ 0xd8487b1f);
store(x.c, seed ^ 0xdbadef9a);
store(x.d, seed);
for (int i = 0; i < 20; ++i)
(void)get_randu(x);
}
// https://burtleburtle.net/bob/rand/smallprng.html
// Returns 32-bit unsigned random numbers.
inline vint spmd_kernel::get_randu(rand_context& x)
{
vint e = x.a - VINT_ROT(x.b, 27);
store(x.a, x.b ^ VINT_ROT(x.c, 17));
store(x.b, x.c + x.d);
store(x.c, x.d + e);
store(x.d, e + x.a);
return x.d;
}
// Returns random numbers between [low, high), or low if low >= high
inline vint spmd_kernel::get_randi(rand_context& x, vint low, vint high)
{
vint rnd = get_randu(x);
vint range = high - low;
vint rnd_range = mulhiu(rnd, range);
return spmd_ternaryi(low < high, low + rnd_range, low);
}
// Returns random numbers between [low, high), or low if low >= high
inline vfloat spmd_kernel::get_randf(rand_context& x, vfloat low, vfloat high)
{
vint rndi = get_randu(x) & 0x7fffff;
vfloat rnd = (vfloat)(rndi) * (1.0f / 8388608.0f);
return spmd_ternaryf(low < high, vfma(high - low, rnd, low), low);
}
CPPSPMD_FORCE_INLINE void spmd_kernel::init_reverse_bits(vint& tab1, vint& tab2)
{
const uint8_t tab1_bytes[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
const uint8_t tab2_bytes[16] = { 0, 8 << 4, 4 << 4, 12 << 4, 2 << 4, 10 << 4, 6 << 4, 14 << 4, 1 << 4, 9 << 4, 5 << 4, 13 << 4, 3 << 4, 11 << 4, 7 << 4, 15 << 4 };
store_all(tab1, init_lookup4(tab1_bytes));
store_all(tab2, init_lookup4(tab2_bytes));
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::reverse_bits(vint k, vint tab1, vint tab2)
{
vint r0 = table_lookup4_8(k & 0x7F7F7F7F, tab2);
vint r1 = table_lookup4_8(VUINT_SHIFT_RIGHT(k, 4) & 0x7F7F7F7F, tab1);
vint r3 = r0 | r1;
return byteswap(r3);
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::count_leading_zeros(vint x)
{
CPPSPMD_DECL(const uint8_t, s_tab[16]) = { 0, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
vint tab = init_lookup4(s_tab);
//x <= 0x0000ffff
vbool c0 = (x & 0xFFFF0000) == 0;
vint n0 = spmd_ternaryi(c0, 16, 0);
vint x0 = spmd_ternaryi(c0, VINT_SHIFT_LEFT(x, 16), x);
//x <= 0x00ffffff
vbool c1 = (x0 & 0xFF000000) == 0;
vint n1 = spmd_ternaryi(c1, n0 + 8, n0);
vint x1 = spmd_ternaryi(c1, VINT_SHIFT_LEFT(x0, 8), x0);
//x <= 0x0fffffff
vbool c2 = (x1 & 0xF0000000) == 0;
vint n2 = spmd_ternaryi(c2, n1 + 4, n1);
vint x2 = spmd_ternaryi(c2, VINT_SHIFT_LEFT(x1, 4), x1);
return table_lookup4_8(VUINT_SHIFT_RIGHT(x2, 28), tab) + n2;
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::count_leading_zeros_alt(vint x)
{
//x <= 0x0000ffff
vbool c0 = (x & 0xFFFF0000) == 0;
vint n0 = spmd_ternaryi(c0, 16, 0);
vint x0 = spmd_ternaryi(c0, VINT_SHIFT_LEFT(x, 16), x);
//x <= 0x00ffffff
vbool c1 = (x0 & 0xFF000000) == 0;
vint n1 = spmd_ternaryi(c1, n0 + 8, n0);
vint x1 = spmd_ternaryi(c1, VINT_SHIFT_LEFT(x0, 8), x0);
//x <= 0x0fffffff
vbool c2 = (x1 & 0xF0000000) == 0;
vint n2 = spmd_ternaryi(c2, n1 + 4, n1);
vint x2 = spmd_ternaryi(c2, VINT_SHIFT_LEFT(x1, 4), x1);
// x <= 0x3fffffff
vbool c3 = (x2 & 0xC0000000) == 0;
vint n3 = spmd_ternaryi(c3, n2 + 2, n2);
vint x3 = spmd_ternaryi(c3, VINT_SHIFT_LEFT(x2, 2), x2);
// x <= 0x7fffffff
vbool c4 = (x3 & 0x80000000) == 0;
return spmd_ternaryi(c4, n3 + 1, n3);
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::count_trailing_zeros(vint x)
{
// cast the least significant bit in v to a float
vfloat f = (vfloat)(x & -x);
// extract exponent and adjust
return VUINT_SHIFT_RIGHT(cast_vfloat_to_vint(f), 23) - 0x7F;
}
CPPSPMD_FORCE_INLINE vint spmd_kernel::count_set_bits(vint x)
{
vint v = x - (VUINT_SHIFT_RIGHT(x, 1) & 0x55555555);
vint v1 = (v & 0x33333333) + (VUINT_SHIFT_RIGHT(v, 2) & 0x33333333);
return VUINT_SHIFT_RIGHT(((v1 + VUINT_SHIFT_RIGHT(v1, 4) & 0xF0F0F0F) * 0x1010101), 24);
}
CPPSPMD_FORCE_INLINE vint cmple_epu16(const vint &a, const vint &b)
{
return cmpeq_epi16(subs_epu16(a, b), vint(0));
}
CPPSPMD_FORCE_INLINE vint cmpge_epu16(const vint &a, const vint &b)
{
return cmple_epu16(b, a);
}
CPPSPMD_FORCE_INLINE vint cmpgt_epu16(const vint &a, const vint &b)
{
return andnot(cmpeq_epi16(a, b), cmple_epu16(b, a));
}
CPPSPMD_FORCE_INLINE vint cmplt_epu16(const vint &a, const vint &b)
{
return cmpgt_epu16(b, a);
}
CPPSPMD_FORCE_INLINE vint cmpge_epi16(const vint &a, const vint &b)
{
return cmpeq_epi16(a, b) | cmpgt_epi16(a, b);
}
CPPSPMD_FORCE_INLINE vint cmple_epi16(const vint &a, const vint &b)
{
return cmpge_epi16(b, a);
}
void spmd_kernel::print_vint(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i));
printf("\n");
}
void spmd_kernel::print_vbool(vbool v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%i ", extract(v, i) ? 1 : 0);
printf("\n");
}
void spmd_kernel::print_vint_hex(vint v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("0x%X ", extract(v, i));
printf("\n");
}
void spmd_kernel::print_active_lanes(const char *pPrefix)
{
CPPSPMD_DECL(int, flags[PROGRAM_COUNT]);
memset(flags, 0, sizeof(flags));
storeu_linear(flags, vint(1));
if (pPrefix)
printf("%s", pPrefix);
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
{
if (flags[i])
printf("%u ", i);
}
printf("\n");
}
void spmd_kernel::print_vfloat(vfloat v)
{
for (uint32_t i = 0; i < PROGRAM_COUNT; i++)
printf("%f ", extract(v, i));
printf("\n");
}
|