diff --git a/include/LinearAlgebra/Vector/vector_operations.h b/include/LinearAlgebra/Vector/vector_operations.h index 2546a6e0..cd877c58 100644 --- a/include/LinearAlgebra/Vector/vector_operations.h +++ b/include/LinearAlgebra/Vector/vector_operations.h @@ -112,18 +112,7 @@ T l1_norm(ConstVector x) return result; } -template -T l2_norm_squared(ConstVector x) -{ - T result = 0.0; - std::size_t n = x.size(); -#pragma omp parallel for reduction(+ : result) if (n > 10'000) - for (std::size_t i = 0; i < n; ++i) { - result += x(i) * x(i); - } - return result; -} - +// Underflow- and overflow-resistant implementation of the L2 norm template T l2_norm(ConstVector x) { @@ -137,17 +126,17 @@ T l2_norm(ConstVector x) scale = abs_val; } } - if (equals(scale, T{0})) { + // 2) if the largest absolute value is zero, the norm is zero + if (scale == T{0}) return T{0}; - } - // 2) accumulate sum of squares of scaled entries + // 3) accumulate sum of squares of scaled entries T sum = 0.0; #pragma omp parallel for reduction(+ : sum) if (n > 10'000) for (std::size_t i = 0; i < n; ++i) { T value = x(i) / scale; sum += value * value; } - // 3) rescale + // 4) rescale return scale * std::sqrt(sum); } diff --git a/tests/LinearAlgebra/Vector/vector_operations.cpp b/tests/LinearAlgebra/Vector/vector_operations.cpp index d411a48f..bf63ddb2 100644 --- a/tests/LinearAlgebra/Vector/vector_operations.cpp +++ b/tests/LinearAlgebra/Vector/vector_operations.cpp @@ -163,28 +163,46 @@ TEST(VectorOperations, l1_vector_norm) EXPECT_DOUBLE_EQ(l1_norm(ConstVector(v)), 8.0); } -/* T l2_norm_squared(ConstVector& x); */ +/* T l2_norm(ConstVector& x); */ -TEST(VectorOperations, l2_vector_norm_squared) +TEST(VectorOperations, l2_vector_norm) { Vector v("v", 3); v(0) = 1; v(1) = -5; v(2) = 2; ConstVector const_v(v); - EXPECT_DOUBLE_EQ(l2_norm_squared(const_v), 30.0); + EXPECT_DOUBLE_EQ(l2_norm(ConstVector(const_v)), std::sqrt(30.0)); } -/* T l2_norm(ConstVector& x); */ +TEST(VectorOperations, zero_l2_vector_norm) +{ + Vector v("v", 3); + v(0) = 0; + v(1) = 0; + v(2) = 0; + ConstVector const_v(v); + EXPECT_DOUBLE_EQ(l2_norm(ConstVector(const_v)), 0.0); +} -TEST(VectorOperations, l2_vector_norm) +TEST(VectorOperations, underflow_l2_vector_norm) { Vector v("v", 3); - v(0) = 1; - v(1) = -5; - v(2) = 2; + v(0) = 1e-300; + v(1) = 1e-300; + v(2) = 1e-300; ConstVector const_v(v); - EXPECT_DOUBLE_EQ(l2_norm(ConstVector(const_v)), std::sqrt(30.0)); + EXPECT_DOUBLE_EQ(l2_norm(ConstVector(const_v)), std::sqrt(3.0) * 1e-300); +} + +TEST(VectorOperations, overflow_l2_vector_norm) +{ + Vector v("v", 3); + v(0) = 1e+300; + v(1) = 1e+300; + v(2) = 1e+300; + ConstVector const_v(v); + EXPECT_DOUBLE_EQ(l2_norm(ConstVector(const_v)), std::sqrt(3.0) * 1e+300); } /* T infinity_norm(ConstVector& x); */