OpenShot Audio Library | OpenShotAudio  0.6.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 
23 namespace juce
24 {
25 
26 //==============================================================================
27 template <typename T>
28 String 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 
48 template <typename T>
49 class ApproximatelyEqualTests final : public UnitTest
50 {
51 public:
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 
364 template<>
365 class ApproximatelyEqualTests<int> final : public UnitTest
366 {
367 public:
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 
414 template <typename T>
415 class IsFiniteTests final : public UnitTest
416 {
417 public:
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 
472 template <typename T>
473 class NextFloatTests final : public UnitTest
474 {
475 public:
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 
519 template <typename Type>
520 struct MathsFloatingPointFunctionsTests
521 {
522  IsFiniteTests<Type> isFiniteTests;
523  NextFloatTests<Type> nextFloatTests;
524  ApproximatelyEqualTests<Type> approximatelyEqualTests;
525 };
526 
527 template<>
528 struct MathsFloatingPointFunctionsTests<int>
529 {
530  ApproximatelyEqualTests<int> approximatelyEqualTests;
531 };
532 
533 struct MathsFunctionsTests
534 {
535  MathsFloatingPointFunctionsTests<int> intFunctionTests;
536  MathsFloatingPointFunctionsTests<float> floatFunctionTests;
537  MathsFloatingPointFunctionsTests<double> doubleFunctionTests;
538  MathsFloatingPointFunctionsTests<long double> longDoubleFunctionTests;
539 };
540 
541 static 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