OpenShot Audio Library | OpenShotAudio 0.4.0
juce_MathsFunctions_test.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26//==============================================================================
27template <typename T>
28String getTemplatedMathsFunctionUnitTestName (const String& functionName)
29{
30 const auto getTypeName = []() -> String
31 {
32 if constexpr (std::is_same_v<int, T>)
33 return "int";
34
35 if constexpr (std::is_same_v<float, T>)
36 return "float";
37
38 if constexpr (std::is_same_v<double, T>)
39 return "double";
40
41 if constexpr (std::is_same_v<long double, T>)
42 return "long double";
43 };
44
45 return functionName + "<" + getTypeName() + ">";
46}
47
48template <typename T>
49class ApproximatelyEqualTests final : public UnitTest
50{
51public:
52 ApproximatelyEqualTests()
53 : UnitTest { getTemplatedMathsFunctionUnitTestName<T> ("approximatelyEqual"), UnitTestCategories::maths }
54 {}
55
56 void runTest() final
57 {
58 using limits = std::numeric_limits<T>;
59
60 constexpr auto zero = T{};
61 constexpr auto one = T (1);
62 constexpr auto min = limits::min();
63 constexpr auto max = limits::max();
64 constexpr auto epsilon = limits::epsilon();
65 constexpr auto oneThird = one / (T) 3;
66
67 beginTest ("Equal values are always equal");
68 {
69 expect (approximatelyEqual (zero, zero));
70 expect (approximatelyEqual (zero, -zero));
71 expect (approximatelyEqual (-zero, -zero));
72
73 expect (approximatelyEqual (min, min));
74 expect (approximatelyEqual (-min, -min));
75
76 expect (approximatelyEqual (one, one));
77 expect (approximatelyEqual (-one, -one));
78
79 expect (approximatelyEqual (max, max));
80 expect (approximatelyEqual (-max, -max));
81
82 const Tolerance<T> zeroTolerance{};
83
84 expect (approximatelyEqual (zero, zero, zeroTolerance));
85 expect (approximatelyEqual (zero, -zero, zeroTolerance));
86 expect (approximatelyEqual (-zero, -zero, zeroTolerance));
87
88 expect (approximatelyEqual (min, min, zeroTolerance));
89 expect (approximatelyEqual (-min, -min, zeroTolerance));
90
91 expect (approximatelyEqual (one, one, zeroTolerance));
92 expect (approximatelyEqual (-one, -one, zeroTolerance));
93
94 expect (approximatelyEqual (max, max, zeroTolerance));
95 expect (approximatelyEqual (-max, -max, zeroTolerance));
96 }
97
98 beginTest ("Comparing subnormal values to zero, returns true");
99 {
100 expect (! exactlyEqual (zero, nextFloatUp (zero)));
101 expect (approximatelyEqual (zero, nextFloatUp (zero)));
102
103 expect (! exactlyEqual (zero, nextFloatDown (zero)));
104 expect (approximatelyEqual (zero, nextFloatDown (zero)));
105
106 expect (! exactlyEqual (zero, nextFloatDown (min)));
107 expect (approximatelyEqual (zero, nextFloatDown (min)));
108
109 expect (! exactlyEqual (zero, nextFloatUp (-min)));
110 expect (approximatelyEqual (zero, nextFloatUp (-min)));
111 }
112
113 beginTest ("Comparing the minimum normal value to zero, returns true");
114 {
115 expect (approximatelyEqual (zero, min));
116 expect (approximatelyEqual (zero, -min));
117 }
118
119 beginTest ("Comparing normal values greater than the minimum to zero, returns true");
120 {
121 expect (! approximatelyEqual (zero, one));
122 expect (! approximatelyEqual (zero, epsilon));
123 expect (! approximatelyEqual (zero, nextFloatUp (min)));
124 expect (! approximatelyEqual (zero, nextFloatDown (-min)));
125 }
126
127 beginTest ("Values with large ranges can be compared");
128 {
129 expect (! approximatelyEqual (zero, max));
130 expect ( approximatelyEqual (zero, max, absoluteTolerance (max)));
131 expect ( approximatelyEqual (zero, max, relativeTolerance (one)));
132 expect (! approximatelyEqual (-one, max));
133 expect (! approximatelyEqual (-max, max));
134 }
135
136 beginTest ("Larger values have a boundary that is a factor of the epsilon");
137 {
138 for (auto exponent = 0; exponent < 127; ++exponent)
139 {
140 const auto value = std::pow ((T) 2, (T) exponent);
141 const auto boundaryValue = value * (one + epsilon);
142
143 expect (juce_isfinite (value));
144 expect (juce_isfinite (boundaryValue));
145
146 expect ( approximatelyEqual (value, boundaryValue));
147 expect (! approximatelyEqual (value, nextFloatUp (boundaryValue)));
148
149 expect ( approximatelyEqual (-value, -boundaryValue));
150 expect (! approximatelyEqual (-value, nextFloatDown (-boundaryValue)));
151 }
152 }
153
154 beginTest ("Tolerances scale with the values being compared");
155 {
156
157 expect (approximatelyEqual ((T) 100'000'000'000'000.01,
158 (T) 100'000'000'000'000.011));
159
160 expect (! approximatelyEqual ((T) 100.01,
161 (T) 100.011));
162
163 expect (! approximatelyEqual ((T) 123'000, (T) 121'000, relativeTolerance ((T) 1e-2)));
164 expect ( approximatelyEqual ((T) 123'000, (T) 122'000, relativeTolerance ((T) 1e-2)));
165 expect ( approximatelyEqual ((T) 123'000, (T) 123'000, relativeTolerance ((T) 1e-2)));
166 expect ( approximatelyEqual ((T) 123'000, (T) 124'000, relativeTolerance ((T) 1e-2)));
167 expect (! approximatelyEqual ((T) 123'000, (T) 125'000, relativeTolerance ((T) 1e-2)));
168
169 expect (! approximatelyEqual ((T) 123, (T) 121, relativeTolerance ((T) 1e-2)));
170 expect ( approximatelyEqual ((T) 123, (T) 122, relativeTolerance ((T) 1e-2)));
171 expect ( approximatelyEqual ((T) 123, (T) 123, relativeTolerance ((T) 1e-2)));
172 expect ( approximatelyEqual ((T) 123, (T) 124, relativeTolerance ((T) 1e-2)));
173 expect (! approximatelyEqual ((T) 123, (T) 125, relativeTolerance ((T) 1e-2)));
174
175 expect (! approximatelyEqual ((T) 12.3, (T) 12.1, relativeTolerance ((T) 1e-2)));
176 expect ( approximatelyEqual ((T) 12.3, (T) 12.2, relativeTolerance ((T) 1e-2)));
177 expect ( approximatelyEqual ((T) 12.3, (T) 12.3, relativeTolerance ((T) 1e-2)));
178 expect ( approximatelyEqual ((T) 12.3, (T) 12.4, relativeTolerance ((T) 1e-2)));
179 expect (! approximatelyEqual ((T) 12.3, (T) 12.5, relativeTolerance ((T) 1e-2)));
180
181 expect (! approximatelyEqual ((T) 1.23, (T) 1.21, relativeTolerance ((T) 1e-2)));
182 expect ( approximatelyEqual ((T) 1.23, (T) 1.22, relativeTolerance ((T) 1e-2)));
183 expect ( approximatelyEqual ((T) 1.23, (T) 1.23, relativeTolerance ((T) 1e-2)));
184 expect ( approximatelyEqual ((T) 1.23, (T) 1.24, relativeTolerance ((T) 1e-2)));
185 expect (! approximatelyEqual ((T) 1.23, (T) 1.25, relativeTolerance ((T) 1e-2)));
186
187 expect (! approximatelyEqual ((T) 0.123, (T) 0.121, relativeTolerance ((T) 1e-2)));
188 expect ( approximatelyEqual ((T) 0.123, (T) 0.122, relativeTolerance ((T) 1e-2)));
189 expect ( approximatelyEqual ((T) 0.123, (T) 0.123, relativeTolerance ((T) 1e-2)));
190 expect ( approximatelyEqual ((T) 0.123, (T) 0.124, relativeTolerance ((T) 1e-2)));
191 expect (! approximatelyEqual ((T) 0.123, (T) 0.125, relativeTolerance ((T) 1e-2)));
192
193 expect (! approximatelyEqual ((T) 0.000123, (T) 0.000121, relativeTolerance ((T) 1e-2)));
194 expect ( approximatelyEqual ((T) 0.000123, (T) 0.000122, relativeTolerance ((T) 1e-2)));
195 expect ( approximatelyEqual ((T) 0.000123, (T) 0.000123, relativeTolerance ((T) 1e-2)));
196 expect ( approximatelyEqual ((T) 0.000123, (T) 0.000124, relativeTolerance ((T) 1e-2)));
197 expect (! approximatelyEqual ((T) 0.000123, (T) 0.000125, relativeTolerance ((T) 1e-2)));
198 }
199
200 beginTest ("The square of the square root of 2 is approximately 2");
201 {
202 constexpr auto two = (T) 2;
203 const auto sqrtOfTwo = std::sqrt (two);
204
205 expect (approximatelyEqual (sqrtOfTwo * sqrtOfTwo, two));
206 expect (approximatelyEqual (-sqrtOfTwo * sqrtOfTwo, -two));
207 expect (approximatelyEqual (two / sqrtOfTwo, sqrtOfTwo));
208 }
209
210 if constexpr (limits::has_quiet_NaN)
211 {
212 beginTest ("Values are never equal to NaN");
213 {
214 const auto nan = limits::quiet_NaN();
215
216 expect (! approximatelyEqual (nan, nan));
217
218 const auto expectNotEqualTo = [&] (auto value)
219 {
220 expect (! approximatelyEqual (value, nan));
221 expect (! approximatelyEqual (nan, value));
222 };
223
224 expectNotEqualTo (zero);
225 expectNotEqualTo (-zero);
226 expectNotEqualTo (min);
227 expectNotEqualTo (-min);
228 expectNotEqualTo (one);
229 expectNotEqualTo (-one);
230 expectNotEqualTo (max);
231 expectNotEqualTo (-max);
232 }
233 }
234
235 if constexpr (limits::has_infinity)
236 {
237 beginTest ("Only infinity is equal to infinity");
238 {
239 const auto inf = limits::infinity();
240 expect (approximatelyEqual (inf, inf));
241 expect (approximatelyEqual (-inf, -inf));
242 expect (! approximatelyEqual (inf, -inf));
243 expect (! approximatelyEqual (-inf, inf));
244
245 const auto expectNotEqualTo = [&] (auto value)
246 {
247 expect (! approximatelyEqual (value, inf));
248 expect (! approximatelyEqual (value, -inf));
249 expect (! approximatelyEqual (inf, value));
250 expect (! approximatelyEqual (-inf, value));
251 };
252
253 expectNotEqualTo (zero);
254 expectNotEqualTo (-zero);
255 expectNotEqualTo (min);
256 expectNotEqualTo (-min);
257 expectNotEqualTo (one);
258 expectNotEqualTo (-one);
259 expectNotEqualTo (max);
260 expectNotEqualTo (-max);
261 }
262 }
263
264 beginTest ("Can set an absolute tolerance");
265 {
266 constexpr std::array<T, 7> negativePowersOfTwo
267 {
268 (T) 0.5 /* 2^-1 */,
269 (T) 0.25 /* 2^-2 */,
270 (T) 0.125 /* 2^-3 */,
271 (T) 0.0625 /* 2^-4 */,
272 (T) 0.03125 /* 2^-5 */,
273 (T) 0.015625 /* 2^-6 */,
274 (T) 0.0078125 /* 2^-7 */
275 };
276
277 const auto testTolerance = [&] (auto tolerance)
278 {
279 const auto t = Tolerance<T>{}.withAbsolute ((T) tolerance);
280
281 const auto testValue= [&] (auto value)
282 {
283 const auto boundary = value + tolerance;
284
285 expect (approximatelyEqual (value, boundary, t));
286 expect (! approximatelyEqual (value, nextFloatUp (boundary), t));
287
288 expect (approximatelyEqual (-value, -boundary, t));
289 expect (! approximatelyEqual (-value, nextFloatDown (-boundary), t));
290 };
291
292 testValue (zero);
293 testValue (min);
294 testValue (epsilon);
295 testValue (one);
296
297 for (const auto value : negativePowersOfTwo)
298 testValue (value);
299 };
300
301 for (const auto toleranceValue : negativePowersOfTwo)
302 testTolerance (toleranceValue);
303 }
304
305 beginTest ("Can set a relative tolerance");
306 {
307 expect (! approximatelyEqual (oneThird, (T) 0.34, relativeTolerance ((T) 1e-2)));
308 expect ( approximatelyEqual (oneThird, (T) 0.334, relativeTolerance ((T) 1e-2)));
309
310 expect (! approximatelyEqual (oneThird, (T) 0.334, relativeTolerance ((T) 1e-3)));
311 expect ( approximatelyEqual (oneThird, (T) 0.3334, relativeTolerance ((T) 1e-3)));
312
313 expect (! approximatelyEqual (oneThird, (T) 0.3334, relativeTolerance ((T) 1e-4)));
314 expect ( approximatelyEqual (oneThird, (T) 0.33334, relativeTolerance ((T) 1e-4)));
315
316 expect (! approximatelyEqual (oneThird, (T) 0.33334, relativeTolerance ((T) 1e-5)));
317 expect ( approximatelyEqual (oneThird, (T) 0.333334, relativeTolerance ((T) 1e-5)));
318
319 expect (! approximatelyEqual (oneThird, (T) 0.333334, relativeTolerance ((T) 1e-6)));
320 expect ( approximatelyEqual (oneThird, (T) 0.3333334, relativeTolerance ((T) 1e-6)));
321
322 expect (! approximatelyEqual (oneThird, (T) 0.3333334, relativeTolerance ((T) 1e-7)));
323 expect ( approximatelyEqual (oneThird, (T) 0.33333334, relativeTolerance ((T) 1e-7)));
324
325 expect ( approximatelyEqual ((T) 1e6, (T) 1e6 + (T) 1, relativeTolerance ((T) 1e-6)));
326 expect (! approximatelyEqual ((T) 1e6, (T) 1e6 + (T) 1, relativeTolerance ((T) 1e-7)));
327
328 expect ( approximatelyEqual ((T) -1e-6, (T) -1.0000009e-6, relativeTolerance ((T) 1e-6)));
329 expect (! approximatelyEqual ((T) -1e-6, (T) -1.0000009e-6, relativeTolerance ((T) 1e-7)));
330
331 const auto a = (T) 1.234567;
332 const auto b = (T) 1.234568;
333
334 for (auto exponent = 0; exponent < 39; ++exponent)
335 {
336 const auto m = std::pow ((T) 10, (T) exponent);
337 expect ( approximatelyEqual (a * m, b * m, relativeTolerance ((T) 1e-6)));
338 expect (! approximatelyEqual (a * m, b * m, relativeTolerance ((T) 1e-7)));
339 }
340 }
341
342 beginTest ("A relative tolerance is always scaled by the maximum value");
343 {
344 expect ( approximatelyEqual ((T) 9, (T) 10, absoluteTolerance ((T) 10.0 * (T) 0.1)));
345 expect (! approximatelyEqual ((T) 9, (T) 10, absoluteTolerance ((T) 9.0 * (T) 0.1)));
346
347 expect (approximatelyEqual ((T) 9, (T) 10, relativeTolerance ((T) 0.1)));
348 expect (approximatelyEqual ((T) 10, (T) 9, relativeTolerance ((T) 0.1)));
349 }
350
351 beginTest ("Documentation examples");
352 {
353 constexpr auto pi = MathConstants<T>::pi;
354
355 expect (! approximatelyEqual (zero, std::sin (pi)));
356 expect ( approximatelyEqual (zero, std::sin (pi), absoluteTolerance (std::sin (pi))));
357
358 expect ( approximatelyEqual ((T) 100, (T) 95, relativeTolerance ((T) 0.05)));
359 expect (! approximatelyEqual ((T) 100, (T) 94, relativeTolerance ((T) 0.05)));
360 }
361 }
362};
363
364template<>
365class ApproximatelyEqualTests<int> final : public UnitTest
366{
367public:
368 ApproximatelyEqualTests()
369 : UnitTest { getTemplatedMathsFunctionUnitTestName<int> ("approximatelyEqual"), UnitTestCategories::maths }
370 {}
371
372 void runTest() final
373 {
374 beginTest ("Identical integers are always equal");
375 {
376 expect (approximatelyEqual ( 0, 0));
377 expect (approximatelyEqual (-0, -0));
378
379 expect (approximatelyEqual ( 1, 1));
380 expect (approximatelyEqual (-1, -1));
381
382 using limits = std::numeric_limits<int>;
383 constexpr auto min = limits::min();
384 constexpr auto max = limits::max();
385
386 expect (approximatelyEqual (min, min));
387 expect (approximatelyEqual (max, max));
388 }
389
390 beginTest ("Non-identical integers are never equal");
391 {
392 expect (! approximatelyEqual ( 0, 1));
393 expect (! approximatelyEqual ( 0, -1));
394
395 expect (! approximatelyEqual ( 1, 2));
396 expect (! approximatelyEqual (-1, -2));
397
398 using limits = std::numeric_limits<int>;
399 constexpr auto min = limits::min();
400 constexpr auto max = limits::max();
401
402 expect (! approximatelyEqual (min, min + 1));
403 expect (! approximatelyEqual (max, max - 1));
404 }
405
406 beginTest ("Zero is equal regardless of the sign");
407 {
408 expect (approximatelyEqual ( 0, -0));
409 expect (approximatelyEqual (-0, 0));
410 }
411 }
412};
413
414template <typename T>
415class IsFiniteTests final : public UnitTest
416{
417public:
418 IsFiniteTests()
419 : UnitTest { getTemplatedMathsFunctionUnitTestName<T> ("juce_isfinite"), UnitTestCategories::maths }
420 {}
421
422 void runTest() final
423 {
424 using limits = std::numeric_limits<T>;
425
426 constexpr auto zero = T{};
427 constexpr auto one = (T) 1;
428 constexpr auto max = limits::max();
429 constexpr auto inf = limits::infinity();
430 constexpr auto nan = limits::quiet_NaN();
431
432 beginTest ("Zero is finite");
433 {
434 expect (juce_isfinite (zero));
435 expect (juce_isfinite (-zero));
436 }
437
438 beginTest ("Subnormals are finite");
439 {
440 expect (juce_isfinite (nextFloatUp (zero)));
441 expect (juce_isfinite (nextFloatDown (zero)));
442 }
443
444 beginTest ("One is finite");
445 {
446 expect (juce_isfinite (one));
447 expect (juce_isfinite (-one));
448 }
449
450 beginTest ("Max is finite");
451 {
452 expect (juce_isfinite (max));
453 expect (juce_isfinite (-max));
454 }
455
456 beginTest ("Infinity is not finite");
457 {
458 expect (! juce_isfinite (inf));
459 expect (! juce_isfinite (-inf));
460 }
461
462 beginTest ("NaN is not finite");
463 {
464 expect (! juce_isfinite (nan));
465 expect (! juce_isfinite (-nan));
466 expect (! juce_isfinite (std::sqrt ((T) -1)));
467 expect (! juce_isfinite (inf * zero));
468 }
469 }
470};
471
472template <typename T>
473class NextFloatTests final : public UnitTest
474{
475public:
476 NextFloatTests()
477 : UnitTest { getTemplatedMathsFunctionUnitTestName<T> ("nextFloat"), UnitTestCategories::maths }
478 {}
479
480 void runTest() final
481 {
482 using limits = std::numeric_limits<T>;
483
484 constexpr auto zero = T{};
485 constexpr auto one = T (1);
486 constexpr auto min = limits::min();
487 constexpr auto epsilon = limits::epsilon();
488
489 beginTest ("nextFloat from zero is subnormal");
490 {
491 expect (juce_isfinite (nextFloatUp (zero)));
492 expect (! exactlyEqual (zero, nextFloatUp (zero)));
493 expect (! std::isnormal (nextFloatUp (zero)));
494
495 expect (juce_isfinite (nextFloatDown (zero)));
496 expect (! exactlyEqual (zero, nextFloatDown (zero)));
497 expect (! std::isnormal (nextFloatDown (zero)));
498 }
499
500 beginTest ("nextFloat from min, towards zero, is subnormal");
501 {
502 expect (std::isnormal (min));
503 expect (std::isnormal (-min));
504 expect (! std::isnormal (nextFloatDown (min)));
505 expect (! std::isnormal (nextFloatUp (-min)));
506 }
507
508 beginTest ("nextFloat from one matches epsilon");
509 {
510 expect (! exactlyEqual (one, nextFloatUp (one)));
511 expect ( exactlyEqual (one + epsilon, nextFloatUp (one)));
512
513 expect (! exactlyEqual (-one, nextFloatDown (-one)));
514 expect ( exactlyEqual (-one - epsilon, nextFloatDown (-one)));
515 }
516 }
517};
518
519template <typename Type>
520struct MathsFloatingPointFunctionsTests
521{
522 IsFiniteTests<Type> isFiniteTests;
523 NextFloatTests<Type> nextFloatTests;
524 ApproximatelyEqualTests<Type> approximatelyEqualTests;
525};
526
527template<>
528struct MathsFloatingPointFunctionsTests<int>
529{
530 ApproximatelyEqualTests<int> approximatelyEqualTests;
531};
532
533struct MathsFunctionsTests
534{
535 MathsFloatingPointFunctionsTests<int> intFunctionTests;
536 MathsFloatingPointFunctionsTests<float> floatFunctionTests;
537 MathsFloatingPointFunctionsTests<double> doubleFunctionTests;
538 MathsFloatingPointFunctionsTests<long double> longDoubleFunctionTests;
539};
540
541static MathsFunctionsTests mathsFunctionsTests;
542
543} // namespace juce
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
static constexpr FloatType pi