Skip to content

Commit

Permalink
That is not how cancellation works.
Browse files Browse the repository at this point in the history
  • Loading branch information
eggrobin committed Sep 18, 2024
1 parent 12f56e7 commit ac31fa3
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 12 deletions.
1 change: 1 addition & 0 deletions numerics/double_precision_body.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ constexpr DoublePrecision<Product<T, U>> VeltkampDekkerProduct(T const& a,
U const hy = y - py + py;
U const ty = y - hy;
// Veltkamp’s 1968 algorithm, as given in [Dek71, p. 234].
// See also exactmul in [Lin81, p. 278].
z = x * y;
zz = (((hx * hy - z) + hx * ty) + tx * hy) + tx * ty;
// Dekker’s algorithm (5.12) would be
Expand Down
12 changes: 6 additions & 6 deletions numerics/root_finders_body.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,13 @@ BoundedArray<Argument, 2> SolveQuadraticEquation(
// Use double precision for the discriminant because there can be
// cancellations. Higham mentions that it is necessary “to use extended
// precision (or some trick tantamount to the use of extended precision).”
DoublePrecision<Discriminant> discriminant = TwoSum(a₁ * a₁, -4.0 * a₀ * a₂);
DoublePrecision<Discriminant> discriminant =
TwoProduct(a₁, a₁) - TwoProduct(4.0 * a₀, a₂);

if (discriminant.value == zero && discriminant.error == zero) {
if (discriminant.value == zero) {
// One solution.
return {origin - 0.5 * a₁ / a₂};
} else if (discriminant.value < zero ||
(discriminant.value == zero && discriminant.error < zero)) {
} else if (discriminant.value < zero) {
// No solution.
return {};
} else {
Expand All @@ -371,9 +371,9 @@ BoundedArray<Argument, 2> SolveQuadraticEquation(
Derivative1 numerator;
constexpr Derivative1 zero{};
if (a₁ > zero) {
numerator = -a₁ - Sqrt(discriminant.value + discriminant.error);
numerator = -a₁ - Sqrt(discriminant.value);
} else {
numerator = -a₁ + Sqrt(discriminant.value + discriminant.error);
numerator = -a₁ + Sqrt(discriminant.value);
}
auto const x₁ = origin + numerator / (2.0 * a₂);
auto const x₂ = origin + (2.0 * a₀) / numerator;
Expand Down
14 changes: 8 additions & 6 deletions numerics/root_finders_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,14 +469,16 @@ TEST_F(RootFindersTest, QuadraticEquations) {
EXPECT_THAT(s3, ElementsAre(-1.0));

// An ill-conditioned system. I fart in its general direction. If done
// naively, this yields {-100032., -99968.4} according to Mathematica.
// naïvely, the results are -100000.0031'0988855 and -99999.9968'90111535
// (separator after the last correct decimal digit), whose error is
// approximately 3.5 million ULPs.
auto const s4 = SolveQuadraticEquation(0.0,
1.0000001e25,
2.0000003e20,
1.0000001e15);
1.000'000'000'000'001e25,
2.000'000'000'000'003e20,
1.000'000'000'000'001e15);
EXPECT_THAT(s4,
ElementsAre(AlmostEquals(-100031.62777541532972762902, 66),
AlmostEquals(-99968.38222458367027247098, 65)));
ElementsAre(AlmostEquals(-100000.00316153381194436811, 0),
AlmostEquals(-99999.996838466282967631886, 1)));

// A typed system.
Instant const t0;
Expand Down

0 comments on commit ac31fa3

Please sign in to comment.