From 394bc6c3dffb3c2dcbb76828ccde09ab08adee0a Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Mon, 30 Mar 2026 19:29:24 +0200 Subject: [PATCH] #12489 Improve robustness of mean calculators Replace asserts with early-return guards so zero or negative weights are silently skipped instead of crashing. Add unit tests for zero-weight and zero-value edge cases in all three calculator classes. Fixes #12489. --- .../RiaWeightedGeometricMeanCalculator.cpp | 4 +- .../RiaWeightedHarmonicMeanCalculator.cpp | 5 +- .../Tools/RiaWeightedMeanCalculator.inl | 8 +-- ...iaWeightedGeometricMeanCalculator-Test.cpp | 28 ++++++++++ ...RiaWeightedHarmonicMeanCalculator-Test.cpp | 54 +++++++++++++++++++ .../UnitTests/RiaWeightedMean-Test.cpp | 36 +++++++++++++ 6 files changed, 122 insertions(+), 13 deletions(-) diff --git a/ApplicationLibCode/Application/Tools/RiaWeightedGeometricMeanCalculator.cpp b/ApplicationLibCode/Application/Tools/RiaWeightedGeometricMeanCalculator.cpp index 5a4148c5e7..37c57f6bb3 100644 --- a/ApplicationLibCode/Application/Tools/RiaWeightedGeometricMeanCalculator.cpp +++ b/ApplicationLibCode/Application/Tools/RiaWeightedGeometricMeanCalculator.cpp @@ -18,8 +18,6 @@ #include "RiaWeightedGeometricMeanCalculator.h" -#include "cvfAssert.h" - #include //-------------------------------------------------------------------------------------------------- @@ -36,7 +34,7 @@ RiaWeightedGeometricMeanCalculator::RiaWeightedGeometricMeanCalculator() //-------------------------------------------------------------------------------------------------- void RiaWeightedGeometricMeanCalculator::addValueAndWeight( double value, double weight ) { - CVF_ASSERT( weight >= 0.0 ); + if ( weight <= 0.0 ) return; // This can be a very big number, consider other algorithms if that becomes a problem m_aggregatedWeightedValue += ( std::log( value ) * weight ); diff --git a/ApplicationLibCode/Application/Tools/RiaWeightedHarmonicMeanCalculator.cpp b/ApplicationLibCode/Application/Tools/RiaWeightedHarmonicMeanCalculator.cpp index 3261a33833..80954e2e38 100644 --- a/ApplicationLibCode/Application/Tools/RiaWeightedHarmonicMeanCalculator.cpp +++ b/ApplicationLibCode/Application/Tools/RiaWeightedHarmonicMeanCalculator.cpp @@ -18,8 +18,6 @@ #include "RiaWeightedHarmonicMeanCalculator.h" -#include "cvfAssert.h" - #include //-------------------------------------------------------------------------------------------------- @@ -36,7 +34,7 @@ RiaWeightedHarmonicMeanCalculator::RiaWeightedHarmonicMeanCalculator() //-------------------------------------------------------------------------------------------------- void RiaWeightedHarmonicMeanCalculator::addValueAndWeight( double value, double weight ) { - CVF_ASSERT( weight > 1.0e-12 && std::abs( value ) > 1.0e-12 ); + if ( weight <= 0.0 || std::abs( value ) < 1.0e-12 ) return; m_aggregatedWeightedValue += weight / value; m_aggregatedWeight += weight; @@ -51,7 +49,6 @@ double RiaWeightedHarmonicMeanCalculator::weightedMean() const { return m_aggregatedWeight / m_aggregatedWeightedValue; } - CVF_ASSERT( false ); return 0.0; } diff --git a/ApplicationLibCode/Application/Tools/RiaWeightedMeanCalculator.inl b/ApplicationLibCode/Application/Tools/RiaWeightedMeanCalculator.inl index 20a3868125..357f7aba26 100644 --- a/ApplicationLibCode/Application/Tools/RiaWeightedMeanCalculator.inl +++ b/ApplicationLibCode/Application/Tools/RiaWeightedMeanCalculator.inl @@ -16,8 +16,6 @@ // ///////////////////////////////////////////////////////////////////////////////// -#include "cvfAssert.h" - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -34,7 +32,7 @@ RiaWeightedMeanCalculator::RiaWeightedMeanCalculator() template void RiaWeightedMeanCalculator::addValueAndWeight( T value, double weight ) { - CVF_ASSERT( weight >= 0.0 ); + if ( weight <= 0.0 ) return; m_aggregatedValue = m_aggregatedValue + value * weight; m_aggregatedWeight += weight; @@ -46,9 +44,7 @@ void RiaWeightedMeanCalculator::addValueAndWeight( T value, double weight ) template T RiaWeightedMeanCalculator::weightedMean() const { - bool validWeights = validAggregatedWeight(); - CVF_TIGHT_ASSERT( validWeights ); - if ( validWeights ) + if ( validAggregatedWeight() ) { return m_aggregatedValue * ( 1.0 / m_aggregatedWeight ); } diff --git a/ApplicationLibCode/UnitTests/RiaWeightedGeometricMeanCalculator-Test.cpp b/ApplicationLibCode/UnitTests/RiaWeightedGeometricMeanCalculator-Test.cpp index 1ad8f6dff1..8763b6050e 100644 --- a/ApplicationLibCode/UnitTests/RiaWeightedGeometricMeanCalculator-Test.cpp +++ b/ApplicationLibCode/UnitTests/RiaWeightedGeometricMeanCalculator-Test.cpp @@ -55,3 +55,31 @@ TEST( RiaWeightedGeometricMeanCalculator, BigValues ) EXPECT_DOUBLE_EQ( 17.0, calc.aggregatedWeight() ); EXPECT_NEAR( expectedValue, calc.weightedMean(), 1e-8 ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaWeightedGeometricMeanCalculator, ZeroWeight ) +{ + { + // Zero weight only - no valid data + RiaWeightedGeometricMeanCalculator calc; + calc.addValueAndWeight( 5.0, 0.0 ); + EXPECT_DOUBLE_EQ( 0.0, calc.aggregatedWeight() ); + EXPECT_DOUBLE_EQ( 0.0, calc.weightedMean() ); + } + + { + // Zero weight mixed with valid data - zero weight is ignored + RiaWeightedGeometricMeanCalculator calc; + calc.addValueAndWeight( 100.0, 0.0 ); + calc.addValueAndWeight( 30.0, 1.5 ); + calc.addValueAndWeight( 60.0, 3.5 ); + calc.addValueAndWeight( 200.0, 0.0 ); + + double expectedValue = std::pow( std::pow( 30.0, 1.5 ) * std::pow( 60.0, 3.5 ), 1 / ( 1.5 + 3.5 ) ); + + EXPECT_DOUBLE_EQ( 5.0, calc.aggregatedWeight() ); + EXPECT_NEAR( expectedValue, calc.weightedMean(), 1e-10 ); + } +} diff --git a/ApplicationLibCode/UnitTests/RiaWeightedHarmonicMeanCalculator-Test.cpp b/ApplicationLibCode/UnitTests/RiaWeightedHarmonicMeanCalculator-Test.cpp index d8f0f60670..581fd9e1cd 100644 --- a/ApplicationLibCode/UnitTests/RiaWeightedHarmonicMeanCalculator-Test.cpp +++ b/ApplicationLibCode/UnitTests/RiaWeightedHarmonicMeanCalculator-Test.cpp @@ -72,3 +72,57 @@ TEST( RiaWeightedHarmonicMeanCalculator, WeightedValues ) EXPECT_NEAR( expectedValue, calc.weightedMean(), 1.0e-8 ); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaWeightedHarmonicMeanCalculator, ZeroWeight ) +{ + { + // Zero weight only - no valid data, no crash + RiaWeightedHarmonicMeanCalculator calc; + calc.addValueAndWeight( 5.0, 0.0 ); + EXPECT_FALSE( calc.validAggregatedWeight() ); + EXPECT_DOUBLE_EQ( 0.0, calc.weightedMean() ); + } + + { + // Zero weight mixed with valid data - zero weight is ignored + RiaWeightedHarmonicMeanCalculator calc; + calc.addValueAndWeight( 100.0, 0.0 ); + calc.addValueAndWeight( 1.0, 1.0 ); + calc.addValueAndWeight( 4.0, 1.0 ); + calc.addValueAndWeight( 4.0, 1.0 ); + calc.addValueAndWeight( 200.0, 0.0 ); + + EXPECT_DOUBLE_EQ( 3.0, calc.aggregatedWeight() ); + EXPECT_NEAR( 2.0, calc.weightedMean(), 1e-10 ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaWeightedHarmonicMeanCalculator, ZeroValue ) +{ + // Zero value is skipped (would cause division by zero) + RiaWeightedHarmonicMeanCalculator calc; + calc.addValueAndWeight( 0.0, 1.0 ); + calc.addValueAndWeight( 1.0, 1.0 ); + calc.addValueAndWeight( 4.0, 1.0 ); + calc.addValueAndWeight( 4.0, 1.0 ); + + EXPECT_DOUBLE_EQ( 3.0, calc.aggregatedWeight() ); + EXPECT_NEAR( 2.0, calc.weightedMean(), 1e-10 ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaWeightedHarmonicMeanCalculator, NoValidData ) +{ + // No valid data added - weightedMean returns 0.0 without asserting + RiaWeightedHarmonicMeanCalculator calc; + EXPECT_FALSE( calc.validAggregatedWeight() ); + EXPECT_DOUBLE_EQ( 0.0, calc.weightedMean() ); +} diff --git a/ApplicationLibCode/UnitTests/RiaWeightedMean-Test.cpp b/ApplicationLibCode/UnitTests/RiaWeightedMean-Test.cpp index 9531460f4a..cec4074f1f 100644 --- a/ApplicationLibCode/UnitTests/RiaWeightedMean-Test.cpp +++ b/ApplicationLibCode/UnitTests/RiaWeightedMean-Test.cpp @@ -29,3 +29,39 @@ TEST( RiaWeightedMeanCalculator, BasicUsage ) EXPECT_DOUBLE_EQ( 5.0, calc.weightedMean() ); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaWeightedMeanCalculator, ZeroWeight ) +{ + { + // Zero weight only - no valid data + RiaWeightedMeanCalculator calc; + calc.addValueAndWeight( 5.0, 0.0 ); + EXPECT_FALSE( calc.validAggregatedWeight() ); + EXPECT_DOUBLE_EQ( 0.0, calc.aggregatedWeight() ); + EXPECT_DOUBLE_EQ( 0.0, calc.weightedMean() ); + } + + { + // Zero weight mixed with valid data - zero weight is ignored + RiaWeightedMeanCalculator calc; + calc.addValueAndWeight( 100.0, 0.0 ); + calc.addValueAndWeight( 5.0, 2.0 ); + calc.addValueAndWeight( 200.0, 0.0 ); + EXPECT_TRUE( calc.validAggregatedWeight() ); + EXPECT_DOUBLE_EQ( 2.0, calc.aggregatedWeight() ); + EXPECT_DOUBLE_EQ( 5.0, calc.weightedMean() ); + } + + { + // Negative weight is also ignored + RiaWeightedMeanCalculator calc; + calc.addValueAndWeight( 100.0, -1.0 ); + calc.addValueAndWeight( 5.0, 2.0 ); + EXPECT_TRUE( calc.validAggregatedWeight() ); + EXPECT_DOUBLE_EQ( 2.0, calc.aggregatedWeight() ); + EXPECT_DOUBLE_EQ( 5.0, calc.weightedMean() ); + } +}