1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

Global: Avoid floating-point equality checks where possible

This commit is contained in:
reuk 2023-03-23 12:02:38 +00:00
parent 081b1ff216
commit 28414a6af8
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
150 changed files with 762 additions and 672 deletions

View file

@ -28,6 +28,9 @@ namespace juce
namespace dsp
{
template <typename SampleType>
String& operator<< (String& str, SIMDRegister<SampleType>) { return str; }
template <typename SampleType>
class AudioBlockUnitTests : public UnitTest
{
@ -79,25 +82,25 @@ public:
resetBlocks();
expect (block != otherBlock);
expect (block.getSample (0, 0) == SampleType (1.0));
expect (block.getSample (0, 4) == SampleType (5.0));
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
expectEquals (block.getSample (0, 0), SampleType (1.0));
expectEquals (block.getSample (0, 4), SampleType (5.0));
expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0));
expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0));
block.swap (otherBlock);
expect (block != otherBlock);
expect (otherBlock.getSample (0, 0) == SampleType (1.0));
expect (otherBlock.getSample (0, 4) == SampleType (5.0));
expect (block.getSample (0, 0) == SampleType (-1.0));
expect (block.getSample (0, 3) == SampleType (-4.0));
expectEquals (otherBlock.getSample (0, 0), SampleType (1.0));
expectEquals (otherBlock.getSample (0, 4), SampleType (5.0));
expectEquals (block.getSample (0, 0), SampleType (-1.0));
expectEquals (block.getSample (0, 3), SampleType (-4.0));
block.swap (otherBlock);
expect (block.getSample (0, 0) == SampleType (1.0));
expect (block.getSample (0, 4) == SampleType (5.0));
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
expectEquals (block.getSample (0, 0), SampleType (1.0));
expectEquals (block.getSample (0, 4), SampleType (5.0));
expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0));
expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0));
}
beginTest ("Getters and setters");
@ -107,49 +110,49 @@ public:
expectEquals ((int) block.getNumChannels(), (int) data.size());
expectEquals ((int) block.getNumSamples(), numSamples);
expect (block.getChannelPointer (0)[2] == SampleType (3.0));
expectEquals (block.getChannelPointer (0)[2], SampleType (3.0));
block.getChannelPointer (0)[2] = SampleType (999.0);
expect (block.getChannelPointer (0)[2] == SampleType (999.0));
expectEquals (block.getChannelPointer (0)[2], SampleType (999.0));
expect (block.getSample (0, 4) == SampleType (5.0));
expect (block.getSample (1, 4) == SampleType (11.0));
expectEquals (block.getSample (0, 4), SampleType (5.0));
expectEquals (block.getSample (1, 4), SampleType (11.0));
expect (block.getSingleChannelBlock (1).getSample (0, 3) == block.getSample (1, 3));
expectEquals (block.getSingleChannelBlock (1).getSample (0, 3), block.getSample (1, 3));
expect (block.getSubsetChannelBlock (0, 2).getSample (1, 3) == block.getSample (1, 3));
expect (block.getSubsetChannelBlock (1, 1).getSample (0, 3) == block.getSample (1, 3));
expectEquals (block.getSubsetChannelBlock (0, 2).getSample (1, 3), block.getSample (1, 3));
expectEquals (block.getSubsetChannelBlock (1, 1).getSample (0, 3), block.getSample (1, 3));
block.setSample (1, 1, SampleType (777.0));
expect (block.getSample (1, 1) == SampleType (777.0));
expectEquals (block.getSample (1, 1), SampleType (777.0));
block.addSample (1, 1, SampleType (1.0));
expect (block.getSample (1, 1) == SampleType (778.0));
expectEquals (block.getSample (1, 1), SampleType (778.0));
}
beginTest ("Basic copying");
{
block.clear();
expect (block.getSample (0, 2) == SampleType (0.0));
expect (block.getSample (1, 4) == SampleType (0.0));
expectEquals (block.getSample (0, 2), SampleType (0.0));
expectEquals (block.getSample (1, 4), SampleType (0.0));
block.fill ((NumericType) 456.0);
expect (block.getSample (0, 2) == SampleType (456.0));
expect (block.getSample (1, 4) == SampleType (456.0));
expectEquals (block.getSample (0, 2), SampleType (456.0));
expectEquals (block.getSample (1, 4), SampleType (456.0));
block.copyFrom (otherBlock);
expect (block != otherBlock);
expect (block.getSample (0, 2) == otherBlock.getSample (0, 2));
expect (block.getSample (1, 4) == otherBlock.getSample (1, 4));
expectEquals (block.getSample (0, 2), otherBlock.getSample (0, 2));
expectEquals (block.getSample (1, 4), otherBlock.getSample (1, 4));
resetBlocks();
SampleType testSample1 = block.getSample (0, 2);
SampleType testSample2 = block.getSample (1, 3);
expect (testSample1 != block.getSample (0, 4));
expect (testSample2 != block.getSample (1, 5));
expectNotEquals (testSample1, block.getSample (0, 4));
expectNotEquals (testSample2, block.getSample (1, 5));
block.move (0, 2);
expect (block.getSample (0, 4) == testSample1);
expect (block.getSample (1, 5) == testSample2);
expectEquals (block.getSample (0, 4), testSample1);
expectEquals (block.getSample (1, 5), testSample2);
}
beginTest ("Addition");
@ -157,22 +160,22 @@ public:
resetBlocks();
block.add ((NumericType) 15.0);
expect (block.getSample (0, 4) == SampleType (20.0));
expect (block.getSample (1, 4) == SampleType (26.0));
expectEquals (block.getSample (0, 4), SampleType (20.0));
expectEquals (block.getSample (1, 4), SampleType (26.0));
block.add (otherBlock);
expect (block.getSample (0, 4) == SampleType (15.0));
expect (block.getSample (1, 4) == SampleType (15.0));
expectEquals (block.getSample (0, 4), SampleType (15.0));
expectEquals (block.getSample (1, 4), SampleType (15.0));
block.replaceWithSumOf (otherBlock, (NumericType) 9.0);
expect (block.getSample (0, 4) == SampleType (4.0));
expect (block.getSample (1, 4) == SampleType (-2.0));
expectEquals (block.getSample (0, 4), SampleType (4.0));
expectEquals (block.getSample (1, 4), SampleType (-2.0));
resetBlocks();
block.replaceWithSumOf (block, otherBlock);
expect (block.getSample (0, 4) == SampleType (0.0));
expect (block.getSample (1, 4) == SampleType (0.0));
expectEquals (block.getSample (0, 4), SampleType (0.0));
expectEquals (block.getSample (1, 4), SampleType (0.0));
}
beginTest ("Subtraction");
@ -180,22 +183,22 @@ public:
resetBlocks();
block.subtract ((NumericType) 15.0);
expect (block.getSample (0, 4) == SampleType (-10.0));
expect (block.getSample (1, 4) == SampleType (-4.0));
expectEquals (block.getSample (0, 4), SampleType (-10.0));
expectEquals (block.getSample (1, 4), SampleType (-4.0));
block.subtract (otherBlock);
expect (block.getSample (0, 4) == SampleType (-5.0));
expect (block.getSample (1, 4) == SampleType (7.0));
expectEquals (block.getSample (0, 4), SampleType (-5.0));
expectEquals (block.getSample (1, 4), SampleType (7.0));
block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0);
expect (block.getSample (0, 4) == SampleType (-14.0));
expect (block.getSample (1, 4) == SampleType (-20.0));
expectEquals (block.getSample (0, 4), SampleType (-14.0));
expectEquals (block.getSample (1, 4), SampleType (-20.0));
resetBlocks();
block.replaceWithDifferenceOf (block, otherBlock);
expect (block.getSample (0, 4) == SampleType (10.0));
expect (block.getSample (1, 4) == SampleType (22.0));
expectEquals (block.getSample (0, 4), SampleType (10.0));
expectEquals (block.getSample (1, 4), SampleType (22.0));
}
beginTest ("Multiplication");
@ -203,22 +206,22 @@ public:
resetBlocks();
block.multiplyBy ((NumericType) 10.0);
expect (block.getSample (0, 4) == SampleType (50.0));
expect (block.getSample (1, 4) == SampleType (110.0));
expectEquals (block.getSample (0, 4), SampleType (50.0));
expectEquals (block.getSample (1, 4), SampleType (110.0));
block.multiplyBy (otherBlock);
expect (block.getSample (0, 4) == SampleType (-250.0));
expect (block.getSample (1, 4) == SampleType (-1210.0));
expectEquals (block.getSample (0, 4), SampleType (-250.0));
expectEquals (block.getSample (1, 4), SampleType (-1210.0));
block.replaceWithProductOf (otherBlock, (NumericType) 3.0);
expect (block.getSample (0, 4) == SampleType (-15.0));
expect (block.getSample (1, 4) == SampleType (-33.0));
expectEquals (block.getSample (0, 4), SampleType (-15.0));
expectEquals (block.getSample (1, 4), SampleType (-33.0));
resetBlocks();
block.replaceWithProductOf (block, otherBlock);
expect (block.getSample (0, 4) == SampleType (-25.0));
expect (block.getSample (1, 4) == SampleType (-121.0));
expectEquals (block.getSample (0, 4), SampleType (-25.0));
expectEquals (block.getSample (1, 4), SampleType (-121.0));
}
beginTest ("Multiply add");
@ -226,12 +229,12 @@ public:
resetBlocks();
block.addProductOf (otherBlock, (NumericType) -1.0);
expect (block.getSample (0, 4) == SampleType (10.0));
expect (block.getSample (1, 4) == SampleType (22.0));
expectEquals (block.getSample (0, 4), SampleType (10.0));
expectEquals (block.getSample (1, 4), SampleType (22.0));
block.addProductOf (otherBlock, otherBlock);
expect (block.getSample (0, 4) == SampleType (35.0));
expect (block.getSample (1, 4) == SampleType (143.0));
expectEquals (block.getSample (0, 4), SampleType (35.0));
expectEquals (block.getSample (1, 4), SampleType (143.0));
}
beginTest ("Negative abs min max");
@ -240,68 +243,68 @@ public:
otherBlock.negate();
block.add (otherBlock);
expect (block.getSample (0, 4) == SampleType (10.0));
expect (block.getSample (1, 4) == SampleType (22.0));
expectEquals (block.getSample (0, 4), SampleType (10.0));
expectEquals (block.getSample (1, 4), SampleType (22.0));
block.replaceWithNegativeOf (otherBlock);
expect (block.getSample (0, 4) == SampleType (-5.0));
expect (block.getSample (1, 4) == SampleType (-11.0));
expectEquals (block.getSample (0, 4), SampleType (-5.0));
expectEquals (block.getSample (1, 4), SampleType (-11.0));
block.clear();
otherBlock.negate();
block.replaceWithAbsoluteValueOf (otherBlock);
expect (block.getSample (0, 4) == SampleType (5.0));
expect (block.getSample (1, 4) == SampleType (11.0));
expectEquals (block.getSample (0, 4), SampleType (5.0));
expectEquals (block.getSample (1, 4), SampleType (11.0));
resetBlocks();
block.replaceWithMinOf (block, otherBlock);
expect (block.getSample (0, 4) == SampleType (-5.0));
expect (block.getSample (1, 4) == SampleType (-11.0));
expectEquals (block.getSample (0, 4), SampleType (-5.0));
expectEquals (block.getSample (1, 4), SampleType (-11.0));
resetBlocks();
block.replaceWithMaxOf (block, otherBlock);
expect (block.getSample (0, 4) == SampleType (5.0));
expect (block.getSample (1, 4) == SampleType (11.0));
expectEquals (block.getSample (0, 4), SampleType (5.0));
expectEquals (block.getSample (1, 4), SampleType (11.0));
resetBlocks();
auto range = block.findMinAndMax();
expect (SampleType (range.getStart()) == SampleType (1.0));
expect (SampleType (range.getEnd()) == SampleType (12.0));
expectEquals (SampleType (range.getStart()), SampleType (1.0));
expectEquals (SampleType (range.getEnd()), SampleType (12.0));
}
beginTest ("Operators");
{
resetBlocks();
block += (NumericType) 10.0;
expect (block.getSample (0, 4) == SampleType (15.0));
expect (block.getSample (1, 4) == SampleType (21.0));
expectEquals (block.getSample (0, 4), SampleType (15.0));
expectEquals (block.getSample (1, 4), SampleType (21.0));
block += otherBlock;
expect (block.getSample (0, 4) == SampleType (10.0));
expect (block.getSample (1, 4) == SampleType (10.0));
expectEquals (block.getSample (0, 4), SampleType (10.0));
expectEquals (block.getSample (1, 4), SampleType (10.0));
resetBlocks();
block -= (NumericType) 10.0;
expect (block.getSample (0, 4) == SampleType (-5.0));
expect (block.getSample (1, 4) == SampleType (1.0));
expectEquals (block.getSample (0, 4), SampleType (-5.0));
expectEquals (block.getSample (1, 4), SampleType (1.0));
block -= otherBlock;
expect (block.getSample (0, 4) == SampleType (0.0));
expect (block.getSample (1, 4) == SampleType (12.0));
expectEquals (block.getSample (0, 4), SampleType (0.0));
expectEquals (block.getSample (1, 4), SampleType (12.0));
resetBlocks();
block *= (NumericType) 10.0;
expect (block.getSample (0, 4) == SampleType (50.0));
expect (block.getSample (1, 4) == SampleType (110.0));
expectEquals (block.getSample (0, 4), SampleType (50.0));
expectEquals (block.getSample (1, 4), SampleType (110.0));
block *= otherBlock;
expect (block.getSample (0, 4) == SampleType (-250.0));
expect (block.getSample (1, 4) == SampleType (-1210.0));
expectEquals (block.getSample (0, 4), SampleType (-250.0));
expectEquals (block.getSample (1, 4), SampleType (-1210.0));
}
beginTest ("Process");
{
resetBlocks();
AudioBlock<SampleType>::process (block, otherBlock, [] (SampleType x) { return x + (NumericType) 1.0; });
expect (otherBlock.getSample (0, 4) == SampleType (6.0));
expect (otherBlock.getSample (1, 4) == SampleType (12.0));
expectEquals (otherBlock.getSample (0, 4), SampleType (6.0));
expectEquals (otherBlock.getSample (1, 4), SampleType (12.0));
}
beginTest ("Copying");

View file

@ -117,19 +117,12 @@ public:
template <typename type>
static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar)
{
#ifdef _MSC_VER
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
#else
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
#endif
alignas (sizeof (SIMDRegister<type>)) type elements[SIMDRegister<type>::SIMDNumElements];
vec.copyToRawArray (elements);
// as we do not want to rely on the access operator we cast this to a primitive pointer
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
if (elements[i] != scalar) return false;
return true;
return std::all_of (std::begin (elements), std::end (elements), [scalar] (const auto x) { return exactlyEqual (x, scalar); });
}
template <typename type>
@ -307,7 +300,7 @@ public:
const SIMDRegister<type>& b = a;
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
u.expect (b[i] == array[i]);
u.expect (exactlyEqual (b[i], array[i]));
}
};
@ -539,8 +532,8 @@ public:
// do check
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
{
array_eq [j] = (array_a[j] == array_b[j]) ? static_cast<MaskType> (-1) : 0;
array_neq [j] = (array_a[j] != array_b[j]) ? static_cast<MaskType> (-1) : 0;
array_eq [j] = ( exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
array_neq [j] = (! exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
array_lt [j] = (array_a[j] < array_b[j]) ? static_cast<MaskType> (-1) : 0;
array_le [j] = (array_a[j] <= array_b[j]) ? static_cast<MaskType> (-1) : 0;
array_gt [j] = (array_a[j] > array_b[j]) ? static_cast<MaskType> (-1) : 0;

View file

@ -145,6 +145,11 @@ typename FIR::Coefficients<FloatType>::Ptr
auto* result = new typename FIR::Coefficients<FloatType> (static_cast<size_t> (N));
auto* c = result->getRawCoefficients();
auto sinc = [] (double x)
{
return approximatelyEqual (x, 0.0) ? 1 : std::sin (x * MathConstants<double>::pi) / (MathConstants<double>::pi * x);
};
if (N % 2 == 1)
{
// Type I
@ -153,9 +158,6 @@ typename FIR::Coefficients<FloatType>::Ptr
Matrix<double> b (M + 1, 1),
q (2 * M + 1, 1);
auto sinc = [] (double x) { return x == 0 ? 1 : std::sin (x * MathConstants<double>::pi)
/ (MathConstants<double>::pi * x); };
auto factorp = wp / MathConstants<double>::pi;
auto factors = ws / MathConstants<double>::pi;
@ -191,9 +193,6 @@ typename FIR::Coefficients<FloatType>::Ptr
Matrix<double> qp (2 * M, 1);
Matrix<double> qs (2 * M, 1);
auto sinc = [] (double x) { return x == 0 ? 1 : std::sin (x * MathConstants<double>::pi)
/ (MathConstants<double>::pi * x); };
auto factorp = wp / MathConstants<double>::pi;
auto factors = ws / MathConstants<double>::pi;

View file

@ -647,7 +647,7 @@ static AudioBuffer<float> resampleImpulseResponse (const AudioBuffer<float>& buf
const double srcSampleRate,
const double destSampleRate)
{
if (srcSampleRate == destSampleRate)
if (approximatelyEqual (srcSampleRate, destSampleRate))
return buf;
const auto factorReading = srcSampleRate / destSampleRate;

View file

@ -97,7 +97,7 @@ class ConvolutionTest : public UnitTest
expect (std::any_of (channel, channel + block.getNumSamples(), [] (float sample)
{
return sample != 0.0f;
return ! approximatelyEqual (sample, 0.0f);
}));
}
}
@ -193,7 +193,7 @@ class ConvolutionTest : public UnitTest
processBlocksWithDiracImpulse();
// Check if the impulse response was loaded
if (block.getSample (0, 1) != 0.0f)
if (! approximatelyEqual (block.getSample (0, 1), 0.0f))
break;
}
}

View file

@ -108,7 +108,7 @@ public:
*/
void setTargetValue (FloatType newValue) noexcept
{
if (newValue == this->target)
if (approximatelyEqual (newValue, this->target))
return;
if (stepsToTarget <= 0)

View file

@ -176,7 +176,7 @@ bool Matrix<ElementType>::solve (Matrix& b) const noexcept
{
auto denominator = A (0,0);
if (denominator == 0)
if (approximatelyEqual (denominator, (ElementType) 0))
return false;
b (0, 0) /= denominator;
@ -187,7 +187,7 @@ bool Matrix<ElementType>::solve (Matrix& b) const noexcept
{
auto denominator = A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0);
if (denominator == 0)
if (approximatelyEqual (denominator, (ElementType) 0))
return false;
auto factor = (1 / denominator);
@ -204,7 +204,7 @@ bool Matrix<ElementType>::solve (Matrix& b) const noexcept
+ A (0, 1) * (A (1, 2) * A (2, 0) - A (1, 0) * A (2, 2))
+ A (0, 2) * (A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0));
if (denominator == 0)
if (approximatelyEqual (denominator, (ElementType) 0))
return false;
auto factor = 1 / denominator;
@ -231,10 +231,10 @@ bool Matrix<ElementType>::solve (Matrix& b) const noexcept
for (size_t j = 0; j < n; ++j)
{
if (M (j, j) == 0)
if (approximatelyEqual (M (j, j), (ElementType) 0))
{
auto i = j;
while (i < n && M (i, j) == 0)
while (i < n && approximatelyEqual (M (i, j), (ElementType) 0))
++i;
if (i == n)

View file

@ -271,7 +271,7 @@ private:
auto value1 = bufferData.getSample (channel, index1);
auto value2 = bufferData.getSample (channel, index2);
auto output = delayFrac == 0 ? value1 : value2 + alpha * (value1 - v[(size_t) channel]);
auto output = approximatelyEqual (delayFrac, (SampleType) 0) ? value1 : value2 + alpha * (value1 - v[(size_t) channel]);
v[(size_t) channel] = output;
return output;

View file

@ -39,8 +39,9 @@ Coefficients<NumericType>& Coefficients<NumericType>::assignImpl (const NumericT
static_assert (Num % 2 == 0, "Must supply an even number of coefficients");
const auto a0Index = Num / 2;
const auto a0 = values[a0Index];
const auto a0Inv = a0 != NumericType() ? static_cast<NumericType> (1) / values[a0Index]
: NumericType();
const auto a0Inv = ! approximatelyEqual (a0, NumericType())
? static_cast<NumericType> (1) / values[a0Index]
: NumericType();
coefficients.clearQuick();
coefficients.ensureStorageAllocated ((int) jmax ((size_t) 8, Num));

View file

@ -755,7 +755,7 @@ void Oversampling<SampleType>::updateDelayLine()
auto latency = getUncompensatedLatency();
fractionalDelay = static_cast<SampleType> (1.0) - (latency - std::floor (latency));
if (fractionalDelay == static_cast<SampleType> (1.0))
if (approximatelyEqual (fractionalDelay, static_cast<SampleType> (1.0)))
fractionalDelay = static_cast<SampleType> (0.0);
else if (fractionalDelay < static_cast<SampleType> (0.618))
fractionalDelay += static_cast<SampleType> (1.0);

View file

@ -48,9 +48,12 @@ struct ProcessSpec
constexpr bool operator== (const ProcessSpec& a, const ProcessSpec& b)
{
return a.sampleRate == b.sampleRate
&& a.maximumBlockSize == b.maximumBlockSize
&& a.numChannels == b.numChannels;
const auto tie = [] (const ProcessSpec& p)
{
return std::tie (p.sampleRate, p.maximumBlockSize, p.numChannels);
};
return tie (a) == tie (b);
}
constexpr bool operator!= (const ProcessSpec& a, const ProcessSpec& b) { return ! (a == b); }

View file

@ -39,7 +39,7 @@ class ProcessorChainTest : public UnitTest
template <typename Context>
void process (const Context& context) noexcept
{
bufferWasClear = context.getInputBlock().getSample (0, 0) == 0;
bufferWasClear = approximatelyEqual (context.getInputBlock().getSample (0, 0), 0.0f);
if (! context.isBypassed)
context.getOutputBlock().add (AddValue);

View file

@ -64,7 +64,7 @@ public:
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds))
{
rampDurationSeconds = newDurationSeconds;
updateRamp();

View file

@ -55,7 +55,7 @@ public:
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds))
{
rampDurationSeconds = newDurationSeconds;
reset();