diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h index 72c2ce014e4..0d214d5677c 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h @@ -82,14 +82,17 @@ struct VarMoments T NO_SANITIZE_UNDEFINED getPopulation() const { - return (m[2] - m[1] * m[1] / m[0]) / m[0]; + /// Due to numerical errors, the result can be slightly less than zero, + /// but it should be impossible. Trim to zero. + + return std::max(T{}, (m[2] - m[1] * m[1] / m[0]) / m[0]); } T NO_SANITIZE_UNDEFINED getSample() const { if (m[0] == 0) return std::numeric_limits::quiet_NaN(); - return (m[2] - m[1] * m[1] / m[0]) / (m[0] - 1); + return std::max(T{}, (m[2] - m[1] * m[1] / m[0]) / (m[0] - 1)); } T NO_SANITIZE_UNDEFINED getMoment3() const @@ -180,7 +183,7 @@ struct VarMomentsDecimal if (common::mulOverflow(getM(1), getM(1), tmp) || common::subOverflow(getM(2), NativeType(tmp / m0), tmp)) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); - return convertFromDecimal, DataTypeNumber>(tmp / m0, scale); + return std::max(Float64{}, convertFromDecimal, DataTypeNumber>(tmp / m0, scale)); } Float64 getSample(UInt32 scale) const @@ -194,7 +197,7 @@ struct VarMomentsDecimal if (common::mulOverflow(getM(1), getM(1), tmp) || common::subOverflow(getM(2), NativeType(tmp / m0), tmp)) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); - return convertFromDecimal, DataTypeNumber>(tmp / (m0 - 1), scale); + return std::max(Float64{}, convertFromDecimal, DataTypeNumber>(tmp / (m0 - 1), scale)); } Float64 getMoment3(UInt32 scale) const diff --git a/tests/queries/0_stateless/01278_variance_nonnegative.reference b/tests/queries/0_stateless/01278_variance_nonnegative.reference new file mode 100644 index 00000000000..405d3348775 --- /dev/null +++ b/tests/queries/0_stateless/01278_variance_nonnegative.reference @@ -0,0 +1,8 @@ +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/tests/queries/0_stateless/01278_variance_nonnegative.sql b/tests/queries/0_stateless/01278_variance_nonnegative.sql new file mode 100644 index 00000000000..aa676d8b269 --- /dev/null +++ b/tests/queries/0_stateless/01278_variance_nonnegative.sql @@ -0,0 +1,9 @@ +SELECT varSamp(0.1) FROM numbers(1000000); +SELECT varPop(0.1) FROM numbers(1000000); +SELECT stddevSamp(0.1) FROM numbers(1000000); +SELECT stddevPop(0.1) FROM numbers(1000000); + +SELECT varSampStable(0.1) FROM numbers(1000000); +SELECT varPopStable(0.1) FROM numbers(1000000); +SELECT stddevSampStable(0.1) FROM numbers(1000000); +SELECT stddevPopStable(0.1) FROM numbers(1000000);