1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.statistics.distribution;
18
19 import org.apache.commons.rng.UniformRandomProvider;
20 import org.apache.commons.rng.simple.RandomSource;
21 import org.junit.jupiter.api.Assertions;
22 import org.junit.jupiter.api.Test;
23
24
25
26
27 class DoubleTolerancesTest {
28 @Test
29 void testEquals() {
30 final DoubleTolerance tol = DoubleTolerances.equals();
31 Assertions.assertEquals("exact", StringUtils.toString(tol));
32
33 Assertions.assertTrue(tol.test(1.5, 1.5));
34 Assertions.assertFalse(tol.test(1.5, 1.51));
35 Assertions.assertFalse(tol.test(1.5, 1.50001));
36 Assertions.assertFalse(tol.test(1.5, Math.nextDown(1.5)));
37 Assertions.assertFalse(tol.test(0.0, -0.0));
38 Assertions.assertFalse(tol.test(0.0, Double.MIN_VALUE));
39 Assertions.assertFalse(tol.test(-Double.MIN_VALUE, Double.MIN_VALUE));
40 Assertions.assertTrue(tol.test(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
41 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
42 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Math.nextDown(Double.MAX_VALUE)));
43 Assertions.assertFalse(tol.test(1.0, Double.POSITIVE_INFINITY));
44 Assertions.assertTrue(tol.test(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
45 Assertions.assertFalse(tol.test(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
46 Assertions.assertFalse(tol.test(-1.0, Double.NEGATIVE_INFINITY));
47 Assertions.assertTrue(tol.test(Double.NaN, Double.NaN));
48 }
49
50
51
52
53 @Test
54 void testUlps0() {
55 final DoubleTolerance tol = DoubleTolerances.ulps(0);
56 Assertions.assertEquals("ulp=0", StringUtils.toString(tol));
57
58 Assertions.assertTrue(tol.test(1.5, 1.5));
59 Assertions.assertFalse(tol.test(1.5, 1.51));
60 Assertions.assertFalse(tol.test(1.5, 1.50001));
61 Assertions.assertFalse(tol.test(1.5, Math.nextDown(1.5)));
62
63 Assertions.assertTrue(tol.test(0.0, -0.0));
64 Assertions.assertFalse(tol.test(0.0, Double.MIN_VALUE));
65 Assertions.assertFalse(tol.test(-Double.MIN_VALUE, Double.MIN_VALUE));
66 Assertions.assertTrue(tol.test(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
67 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
68 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Math.nextDown(Double.MAX_VALUE)));
69 Assertions.assertFalse(tol.test(1.0, Double.POSITIVE_INFINITY));
70 Assertions.assertTrue(tol.test(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
71 Assertions.assertFalse(tol.test(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
72 Assertions.assertFalse(tol.test(-1.0, Double.NEGATIVE_INFINITY));
73 Assertions.assertTrue(tol.test(Double.NaN, Double.NaN));
74 }
75
76 @Test
77 void testUlps1() {
78 final DoubleTolerance tol = DoubleTolerances.ulps(1);
79 Assertions.assertEquals("ulp=1", StringUtils.toString(tol));
80
81 Assertions.assertTrue(tol.test(1.5, 1.5));
82 Assertions.assertFalse(tol.test(1.5, 1.51));
83 Assertions.assertFalse(tol.test(1.5, 1.501));
84 Assertions.assertFalse(tol.test(15, 15.01));
85 Assertions.assertTrue(tol.test(1.5, Math.nextDown(1.5)));
86 Assertions.assertTrue(tol.test(0.0, -0.0));
87 Assertions.assertTrue(tol.test(0.0, Double.MIN_VALUE));
88 Assertions.assertFalse(tol.test(-Double.MIN_VALUE, Double.MIN_VALUE));
89 Assertions.assertTrue(tol.test(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
90 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
91 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Math.nextDown(Double.MAX_VALUE)));
92 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Math.nextDown(Math.nextDown(Double.MAX_VALUE))));
93 Assertions.assertFalse(tol.test(1.0, Double.POSITIVE_INFINITY));
94 Assertions.assertTrue(tol.test(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
95 Assertions.assertTrue(tol.test(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
96 Assertions.assertFalse(tol.test(-1.0, Double.NEGATIVE_INFINITY));
97 Assertions.assertTrue(tol.test(Double.NaN, Double.NaN));
98 }
99
100 @Test
101 void testAbsolute() {
102 final double eps = 1e-3;
103 final DoubleTolerance tol = DoubleTolerances.absolute(eps);
104 Assertions.assertEquals("abs=" + eps, StringUtils.toString(tol));
105
106 Assertions.assertTrue(tol.test(1.5, 1.5));
107 Assertions.assertFalse(tol.test(1.5, 1.51));
108 Assertions.assertTrue(tol.test(1.5, 1.501));
109
110 Assertions.assertFalse(tol.test(15, 15.01));
111 Assertions.assertTrue(tol.test(1.5, Math.nextDown(1.5)));
112 Assertions.assertTrue(tol.test(0.0, -0.0));
113 Assertions.assertTrue(tol.test(0.0, Double.MIN_VALUE));
114 Assertions.assertTrue(tol.test(-Double.MIN_VALUE, Double.MIN_VALUE));
115 Assertions.assertTrue(tol.test(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
116 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
117
118 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Math.nextDown(Double.MAX_VALUE)));
119
120 Assertions.assertFalse(tol.test(Double.MAX_VALUE, Math.nextDown(Math.nextDown(Double.MAX_VALUE))));
121 Assertions.assertFalse(tol.test(1.0, Double.POSITIVE_INFINITY));
122 Assertions.assertTrue(tol.test(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
123 Assertions.assertTrue(tol.test(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
124 Assertions.assertFalse(tol.test(-1.0, Double.NEGATIVE_INFINITY));
125 Assertions.assertTrue(tol.test(Double.NaN, Double.NaN));
126 }
127
128 @Test
129 void testRelative() {
130 final double eps = 1e-3;
131 final DoubleTolerance tol = DoubleTolerances.relative(eps);
132 Assertions.assertEquals("rel=" + eps, StringUtils.toString(tol));
133
134 Assertions.assertTrue(tol.test(1.5, 1.5));
135 Assertions.assertFalse(tol.test(1.5, 1.51));
136 Assertions.assertTrue(tol.test(1.5, 1.501));
137
138 Assertions.assertTrue(tol.test(0.15, 0.1501));
139 Assertions.assertTrue(tol.test(1.5, Math.nextDown(1.5)));
140 Assertions.assertTrue(tol.test(0.0, -0.0));
141 Assertions.assertTrue(tol.test(0.0, Double.MIN_VALUE));
142
143 Assertions.assertFalse(tol.test(-Double.MIN_VALUE, Double.MIN_VALUE));
144 Assertions.assertTrue(tol.test(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
145 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
146
147 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Math.nextDown(Double.MAX_VALUE)));
148 Assertions.assertTrue(tol.test(Double.MAX_VALUE, Math.nextDown(Math.nextDown(Double.MAX_VALUE))));
149 Assertions.assertFalse(tol.test(1.0, Double.POSITIVE_INFINITY));
150 Assertions.assertTrue(tol.test(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
151 Assertions.assertTrue(tol.test(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
152 Assertions.assertFalse(tol.test(-1.0, Double.NEGATIVE_INFINITY));
153
154 Assertions.assertFalse(tol.test(Double.NaN, Double.NaN));
155 }
156
157 @Test
158 void testMessageFormatting() {
159 final DoubleTolerance t1 = DoubleTolerances.equals();
160 final DoubleTolerance t2 = DoubleTolerances.ulps(1);
161 final DoubleTolerance t3 = DoubleTolerances.absolute(1e-3);
162 final DoubleTolerance t4 = DoubleTolerances.relative(1e-4);
163 final String s1 = StringUtils.toString(t1);
164 final String s2 = StringUtils.toString(t2);
165 final String s3 = StringUtils.toString(t3);
166 final String s4 = StringUtils.toString(t4);
167
168
169
170
171 Assertions.assertEquals(s1 + " || " + s2, StringUtils.toString(t1.or(t2)));
172 Assertions.assertEquals(s2 + " || " + s1, StringUtils.toString(t2.or(t1)));
173 Assertions.assertEquals(s3 + " || " + s4, StringUtils.toString(t3.or(t4)));
174 Assertions.assertEquals(s3 + " && " + s4, StringUtils.toString(t3.and(t4)));
175 Assertions.assertEquals(s3 + " && " + s4 + " && " + s1,
176 StringUtils.toString(t3.and(t4).and(t1)));
177 Assertions.assertEquals(s3 + " || " + s4 + " || " + s1,
178 StringUtils.toString(t3.or(t4).or(t1)));
179 Assertions.assertEquals("(" + s3 + " && " + s4 + ") || " + s1,
180 StringUtils.toString(t3.and(t4).or(t1)));
181 Assertions.assertEquals("(" + s3 + " && " + s4 + ") || (" + s1 + " && " + s2 + ")",
182 StringUtils.toString(t3.and(t4).or(t1.and(t2))));
183 Assertions.assertEquals(s3 + " && (" + s1 + " || " + s2 + ")",
184 StringUtils.toString(t3.and(t1.or(t2))));
185
186
187 Assertions.assertEquals("!(" + s1 + ")", StringUtils.toString(t1.negate()));
188 Assertions.assertEquals(s1, StringUtils.toString(t1.negate().negate()));
189 Assertions.assertEquals(s1 + " || !(" + s2 + ")", StringUtils.toString(t1.or(t2.negate())));
190 Assertions.assertEquals("!(" + s1 + " || " + s2 + ")", StringUtils.toString(t1.or(t2).negate()));
191 }
192
193 @Test
194 void testCombinations() {
195 final DoubleTolerance eq = DoubleTolerances.equals();
196 final DoubleTolerance ulp = DoubleTolerances.ulps(2);
197 final DoubleTolerance abs = DoubleTolerances.absolute(1e-3);
198 final DoubleTolerance rel = DoubleTolerances.relative(1e-6);
199
200
201
202
203 double a = 0.12345;
204 double b = 0.123456;
205 double c = Double.MIN_VALUE;
206 double d = -c;
207 double e = 12345.67;
208 double f = 12345.678;
209
210
211 Assertions.assertTrue(eq.test(a, a));
212 Assertions.assertTrue(ulp.test(a, a));
213 Assertions.assertTrue(abs.test(a, a));
214 Assertions.assertTrue(abs.test(a, b));
215 Assertions.assertFalse(eq.negate().test(a, a));
216 Assertions.assertFalse(ulp.negate().test(a, a));
217 Assertions.assertFalse(abs.negate().test(a, a));
218 Assertions.assertFalse(abs.negate().test(a, b));
219
220 Assertions.assertFalse(eq.test(a, b));
221 Assertions.assertFalse(ulp.test(a, b));
222 Assertions.assertTrue(abs.test(a, b), "absolute");
223 Assertions.assertFalse(rel.test(a, b));
224
225 Assertions.assertTrue(eq.test(c, c));
226 Assertions.assertTrue(ulp.test(c, c));
227 Assertions.assertTrue(abs.test(c, c));
228 Assertions.assertTrue(abs.test(c, c));
229
230 Assertions.assertFalse(eq.test(c, d));
231 Assertions.assertTrue(ulp.test(c, d), "ulp");
232 Assertions.assertTrue(abs.test(c, d), "absolute");
233 Assertions.assertFalse(rel.test(c, d));
234
235 Assertions.assertTrue(eq.test(e, e));
236 Assertions.assertTrue(ulp.test(e, e));
237 Assertions.assertTrue(abs.test(e, e));
238 Assertions.assertTrue(abs.test(e, e));
239
240 Assertions.assertFalse(eq.test(e, f));
241 Assertions.assertFalse(ulp.test(e, f));
242 Assertions.assertFalse(abs.test(e, f));
243 Assertions.assertTrue(rel.test(e, f), "relative");
244
245
246
247 final DoubleTolerance[] tol = {eq, ulp, abs, rel};
248 final double[][] pairs = {{a, b}, {c, d}, {e, f}};
249 for (final double[] pair : pairs) {
250 final double x = pair[0];
251 final double y = pair[1];
252 for (final DoubleTolerance t1 : tol) {
253 final boolean ex1 = t1.test(x, y);
254 for (final DoubleTolerance t2 : tol) {
255 final boolean ex2 = t2.test(x, y);
256
257
258
259
260 Assertions.assertEquals(ex1 || ex2, t1.or(t2).test(x, y),
261 () -> String.format("%s or %s test (%s, %s", t1, t2, x, y));
262 Assertions.assertEquals(ex1 && ex2, t1.and(t2).test(x, y),
263 () -> String.format("%s and %s test (%s, %s", t1, t2, x, y));
264
265 for (final DoubleTolerance t3 : tol) {
266 final boolean ex3 = t3.test(x, y);
267 Assertions.assertEquals((ex1 || ex2) && ex3, t1.or(t2).and(t3).test(x, y),
268 () -> String.format("(%s or %s) and %s test (%s, %s", t1, t2, t3, x, y));
269 Assertions.assertEquals((ex1 || ex2) || ex3, t1.or(t2).or(t3).test(x, y),
270 () -> String.format("(%s or %s) or %s test (%s, %s", t1, t2, t3, x, y));
271 Assertions.assertEquals((ex1 && ex2) && ex3, t1.and(t2).and(t3).test(x, y),
272 () -> String.format("(%s and %s) and %s test (%s, %s", t1, t2, t3, x, y));
273 Assertions.assertEquals((ex1 && ex2) || ex3, t1.and(t2).or(t3).test(x, y),
274 () -> String.format("(%s and %s) or %s test (%s, %s", t1, t2, t3, x, y));
275
276 Assertions.assertEquals(ex1 || (ex2 && ex3), t1.or(t2.and(t3)).test(x, y),
277 () -> String.format("%s or (%s and %s) test (%s, %s", t1, t2, t3, x, y));
278 Assertions.assertEquals(ex1 || (ex2 || ex3), t1.or(t2.or(t3)).test(x, y),
279 () -> String.format("%s or (%s or %s) test (%s, %s", t1, t2, t3, x, y));
280 Assertions.assertEquals(ex1 && (ex2 && ex3), t1.and(t2.and(t3)).test(x, y),
281 () -> String.format("%s and (%s and %s) test (%s, %s", t1, t2, t3, x, y));
282 Assertions.assertEquals(ex1 && (ex2 || ex3), t1.and(t2.or(t3)).test(x, y),
283 () -> String.format("%s and (%s or %s) test (%s, %s", t1, t2, t3, x, y));
284 }
285 }
286 }
287 }
288 }
289
290 @Test
291 void testSymmetry() {
292 final int maxUlps = 4;
293 final double absTolerance = 1e-3;
294 final double relTolerance = 1e-6;
295 final DoubleTolerance eq = DoubleTolerances.equals();
296 final DoubleTolerance ulp = DoubleTolerances.ulps(maxUlps);
297 final DoubleTolerance abs = DoubleTolerances.absolute(absTolerance);
298 final DoubleTolerance rel = DoubleTolerances.relative(relTolerance);
299 final UniformRandomProvider rng = RandomSource.JSF_64.create();
300 final long one = Double.doubleToLongBits(1.0);
301
302 for (final int exp : new int[] {
303
304 -1074, -1073, -1066, -1022, -567, -123, -20,
305
306 -8, -7, -6, -5, -4, -3, -2, -1,
307
308 0, 1, 2, 3, 4, 8, 156, 1022, 1023
309 }) {
310 final double scale = Math.scalb(1.0, exp);
311 for (int i = 0; i < 50; i++) {
312
313
314 final double u = Double.longBitsToDouble(one | (rng.nextLong() >>> 12));
315 Assertions.assertTrue(u >= 1.0 && u < 2.0, "Unexpected range");
316
317 final double x = u * scale;
318 Assertions.assertTrue(x > 0 && x <= Double.MAX_VALUE, () -> "Unexpected scaled number: " + x);
319
320
321 final double dx1 = Math.ulp(x) * maxUlps;
322 final double dx2 = absTolerance;
323 final double dx3 = x * relTolerance;
324 final double[] ys = {
325 x,
326 x + dx1 * 0.5, x + dx1, x + dx1 * 2, x - dx1 * 0.5, x - dx1, x - dx1 * 2,
327 x + dx2 * 0.5, x + dx2, x + dx2 * 2, x - dx2 * 0.5, x - dx2, x - dx2 * 2,
328 x + dx3 * 0.5, x + dx3, x + dx3 * 2, x - dx3 * 0.5, x - dx3, x - dx3 * 2,
329 };
330 for (final double y : ys) {
331 Assertions.assertEquals(eq.test(x, y), eq.test(y, x), "eq");
332 Assertions.assertEquals(ulp.test(x, y), ulp.test(y, x), "ulp");
333 Assertions.assertEquals(abs.test(x, y), abs.test(y, x), "abs");
334 Assertions.assertEquals(rel.test(x, y), rel.test(y, x), "rel");
335
336 Assertions.assertEquals(eq.test(x, y), eq.test(-y, -x), "eq");
337 Assertions.assertEquals(ulp.test(x, y), ulp.test(-y, -x), "ulp");
338 Assertions.assertEquals(abs.test(x, y), abs.test(-y, -x), "abs");
339 Assertions.assertEquals(rel.test(x, y), rel.test(-y, -x), "rel");
340 }
341 }
342 }
343 }
344 }