Skip to content

Commit 11860a9

Browse files
authored
Correct under- and overflow protection in l2_norm (#184)
1 parent 39ce1dd commit 11860a9

File tree

2 files changed

+32
-25
lines changed

2 files changed

+32
-25
lines changed

include/LinearAlgebra/Vector/vector_operations.h

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,7 @@ T l1_norm(ConstVector<T> x)
112112
return result;
113113
}
114114

115-
template <typename T>
116-
T l2_norm_squared(ConstVector<T> x)
117-
{
118-
T result = 0.0;
119-
std::size_t n = x.size();
120-
#pragma omp parallel for reduction(+ : result) if (n > 10'000)
121-
for (std::size_t i = 0; i < n; ++i) {
122-
result += x(i) * x(i);
123-
}
124-
return result;
125-
}
126-
115+
// Underflow- and overflow-resistant implementation of the L2 norm
127116
template <typename T>
128117
T l2_norm(ConstVector<T> x)
129118
{
@@ -137,17 +126,17 @@ T l2_norm(ConstVector<T> x)
137126
scale = abs_val;
138127
}
139128
}
140-
if (equals(scale, T{0})) {
129+
// 2) if the largest absolute value is zero, the norm is zero
130+
if (scale == T{0})
141131
return T{0};
142-
}
143-
// 2) accumulate sum of squares of scaled entries
132+
// 3) accumulate sum of squares of scaled entries
144133
T sum = 0.0;
145134
#pragma omp parallel for reduction(+ : sum) if (n > 10'000)
146135
for (std::size_t i = 0; i < n; ++i) {
147136
T value = x(i) / scale;
148137
sum += value * value;
149138
}
150-
// 3) rescale
139+
// 4) rescale
151140
return scale * std::sqrt(sum);
152141
}
153142

tests/LinearAlgebra/Vector/vector_operations.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,28 +163,46 @@ TEST(VectorOperations, l1_vector_norm)
163163
EXPECT_DOUBLE_EQ(l1_norm(ConstVector<double>(v)), 8.0);
164164
}
165165

166-
/* T l2_norm_squared(ConstVector<T>& x); */
166+
/* T l2_norm(ConstVector<T>& x); */
167167

168-
TEST(VectorOperations, l2_vector_norm_squared)
168+
TEST(VectorOperations, l2_vector_norm)
169169
{
170170
Vector<double> v("v", 3);
171171
v(0) = 1;
172172
v(1) = -5;
173173
v(2) = 2;
174174
ConstVector<double> const_v(v);
175-
EXPECT_DOUBLE_EQ(l2_norm_squared(const_v), 30.0);
175+
EXPECT_DOUBLE_EQ(l2_norm(ConstVector<double>(const_v)), std::sqrt(30.0));
176176
}
177177

178-
/* T l2_norm(ConstVector<T>& x); */
178+
TEST(VectorOperations, zero_l2_vector_norm)
179+
{
180+
Vector<double> v("v", 3);
181+
v(0) = 0;
182+
v(1) = 0;
183+
v(2) = 0;
184+
ConstVector<double> const_v(v);
185+
EXPECT_DOUBLE_EQ(l2_norm(ConstVector<double>(const_v)), 0.0);
186+
}
179187

180-
TEST(VectorOperations, l2_vector_norm)
188+
TEST(VectorOperations, underflow_l2_vector_norm)
181189
{
182190
Vector<double> v("v", 3);
183-
v(0) = 1;
184-
v(1) = -5;
185-
v(2) = 2;
191+
v(0) = 1e-300;
192+
v(1) = 1e-300;
193+
v(2) = 1e-300;
186194
ConstVector<double> const_v(v);
187-
EXPECT_DOUBLE_EQ(l2_norm(ConstVector<double>(const_v)), std::sqrt(30.0));
195+
EXPECT_DOUBLE_EQ(l2_norm(ConstVector<double>(const_v)), std::sqrt(3.0) * 1e-300);
196+
}
197+
198+
TEST(VectorOperations, overflow_l2_vector_norm)
199+
{
200+
Vector<double> v("v", 3);
201+
v(0) = 1e+300;
202+
v(1) = 1e+300;
203+
v(2) = 1e+300;
204+
ConstVector<double> const_v(v);
205+
EXPECT_DOUBLE_EQ(l2_norm(ConstVector<double>(const_v)), std::sqrt(3.0) * 1e+300);
188206
}
189207

190208
/* T infinity_norm(ConstVector<T>& x); */

0 commit comments

Comments
 (0)