-
Notifications
You must be signed in to change notification settings - Fork 3
/
neighbors.d
123 lines (109 loc) · 4.06 KB
/
neighbors.d
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
module mach.math.floats.neighbors;
private:
import mach.traits : isFloatingPoint, IEEEFormatOf;
import mach.math.bits.pow2 : pow2, pow2d;
import mach.math.floats.extract : fextractsgn, fextractexp, fextractsig;
import mach.math.floats.inject : finjectsig, finjectsgn, fcompose, fcopysgn;
import mach.math.floats.properties : fisinf, fisnan;
public:
/// Get the successor of a floating point number.
/// That is, the smallest representable value which is greater than the input.
/// Returns +inf if the input is +inf or the largest representable value.
/// Returns the smallest representable finite value if the input is -inf.
/// Returns NaN if the input is NaN.
auto fsuccessor(T)(in T value) if(isFloatingPoint!T){
if(value < 0) return value.fmagpredecessor;
else return value.fmagsuccessor.finjectsgn(0);
}
/// Get the successor of a floating point number,
/// regarding magnitude but not respecting sign.
auto fmagsuccessor(T)(in T value) if(isFloatingPoint!T){
if(value.fisinf){
return fcopysgn(value, T.infinity);
}else if(value.fisnan){
return value;
}else{
enum Format = IEEEFormatOf!T;
enum sigsize = Format.sigsize;
auto sgn = value.fextractsgn;
auto exp = value.fextractexp;
auto sig = value.fextractsig;
if(sig < pow2d!sigsize){
return value.finjectsig(sig + 1);
}else if(exp < Format.expmax - 1){
static if(Format.intpart){
return fcompose!T(sgn, exp + 1, pow2!(sigsize - 1));
}else{
return fcompose!T(sgn, exp + 1, 0);
}
}else{
return fcopysgn(value, T.infinity);
}
}
}
/// Get the predecessor of a floating point number.
/// That is, the greatest representable value which is smaller than the input.
/// Returns -inf if the input is -inf or the smallest representable value.
/// Returns the greatest representable finite value if the input is +inf.
/// Returns NaN if the input is NaN.
auto fpredecessor(T)(in T value) if(isFloatingPoint!T){
if(value > 0) return value.fmagpredecessor;
else return value.fmagsuccessor.finjectsgn(1);
}
/// Get the predecessor of a floating point number,
/// regarding magnitude but not respecting sign.
auto fmagpredecessor(T)(in T value) if(isFloatingPoint!T){
if(value.fisinf){
return fcopysgn(value, T.max);
}else if(value.fisnan){
return value;
}else{
enum Format = IEEEFormatOf!T;
enum sigsize = Format.sigsize;
auto sgn = value.fextractsgn;
auto exp = value.fextractexp;
auto sig = value.fextractsig;
static if(Format.intpart){
if(sig > pow2!(sigsize - 1) || exp == 0){
return value.finjectsig(sig - 1);
}else{
return fcompose!T(sgn, exp - 1, pow2d!sigsize);
}
}else{
if(sig > 0){
return value.finjectsig(sig - 1);
}else{
return fcompose!T(sgn, exp - 1, pow2d!sigsize);
}
}
}
}
version(unittest){
private:
import mach.test;
import mach.meta : Aliases;
}
unittest{
tests("Float neighbors", {
foreach(T; Aliases!(float, double, real)){
tests(T.stringof, {
testeq(T.infinity.fsuccessor, T.infinity);
testeq((-T.infinity).fsuccessor, -T.max);
testeq(T.max.fsuccessor, T.infinity);
testeq(T(0).fsuccessor.fextractsig, 1);
testeq(T.infinity.fpredecessor, T.max);
testeq((-T.infinity).fpredecessor, -T.infinity);
testeq((-T.max).fpredecessor, -T.infinity);
T[] narray = [
0.0, -0.0, 1, -1, 10, -10, 10.55, -10.55,
40.125, -40.125, 256, -256, 12345.6, -12345.6,
T.max, -T.max, T.min_normal, -T.min_normal
];
foreach(n; narray){
testeq(n.fsuccessor.fpredecessor, n);
testeq(n.fpredecessor.fsuccessor, n);
}
});
}
});
}