-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
02-19_doz_max_min.go
41 lines (33 loc) · 1.6 KB
/
02-19_doz_max_min.go
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
package hd
// DifferenceOrZero (doz) returns x - y if x > y else 0.
// It computes in 7-10 branch-free RISC instructions.
// Note, smaller DifferenceOrZero version with comparison is skipped as Go lacks comparison returning int.
func DifferenceOrZero[T Signed](x, y T) T {
d := x - y
return d & (^(d ^ ((x ^ y) & (d ^ x))) >> 64)
}
func Max[T Signed](x, y T) T { return y + DifferenceOrZero(x, y) }
func Min[T Signed](x, y T) T { return x - DifferenceOrZero(x, y) }
// DifferenceOrZeroUnsigned (dozu) computes in 7-10 branch-free RISC instructions.
// Note, smaller DOZ version with comparison is skipped as Go lacks comparison returning int.
func DifferenceOrZeroUnsigned(x, y uint32) uint32 {
d := x - y
return d & ^(ShiftRightSignedFromUnsigned(((^x & y) | (^(x ^ y) & d)), 31))
}
// DifferenceOrZeroRanges requires
// signed x and y be in range [-2^30, 2^30-1]
// and unsigned x and y be in range [0, 2^31-1]
func DifferenceOrZeroRanges[T int32 | uint32](x, y T) T {
return (x - y) & ^(ShiftRightSigned32((x - y), 31))
}
// MaxRanges requires
// signed x and y be in range [-2^30, 2^30-1]
// and unsigned x and y be in range [0, 2^31-1]
func MaxRanges[T int32 | uint32](x, y T) T { return x - ((x - y) & (ShiftRightSigned32((x - y), 31))) }
// MinRanges requires
// signed x and y be in range [-2^30, 2^30-1]
// and unsigned x and y be in range [0, 2^31-1]
func MinRanges[T int32 | uint32](x, y T) T { return y + ((x - y) & (ShiftRightSigned32((x - y), 31))) }
// TODO: expose assembly doz if detected on platform, for very high efficiency
// TODO: version with conditional move instructions
// TODO: version with carry