// SPDX-FileCopyrightText: The Eigen Authors // SPDX-License-Identifier: MPL-2.0 #include "main.h" #include // -ffinite-math-only lets the compiler assume every result is finite, so isfinite(), isinf(), and isnan() fold to // constants in this translation unit and cannot be used to check a result. A comparison against a finite bound is // still evaluated, and rejects infinities and NaNs alike. template bool is_representable(const RealScalar& x) { return numext::abs(x) <= NumTraits::highest(); } // makeHouseholder must reach the scaled path whenever the direct construction would square the input out of range. // The dispatch has to be decided from the input magnitudes, because a test on the overflowed sum does not survive // here. template void householder_overflow(const Matrix& vector) { typedef Matrix VectorType; typedef Matrix MatrixType; const Index size = vector.size(); VectorType essential(size - 1); RealScalar tau; RealScalar beta; vector.makeHouseholder(essential, tau, beta); VERIFY(is_representable(tau)); VERIFY(is_representable(beta)); VERIFY(is_representable(essential.cwiseAbs().maxCoeff())); // tau and the essential vector are scale invariant and beta is homogeneous, so applying the reflector to a // power-of-two rescaling of the input is exact and keeps the check itself inside the range. int exponent = 0; (void)std::frexp(vector.cwiseAbs().maxCoeff(), &exponent); const RealScalar scale = std::ldexp(RealScalar(1), exponent); const VectorType scaled = vector / scale; VectorType v(size); v[0] = RealScalar(1); v.tail(size - 1) = essential; const MatrixType reflector = MatrixType::Identity(size, size) - tau * v * v.transpose(); VERIFY_IS_APPROX(reflector.transpose() * reflector, MatrixType::Identity(size, size)); const VectorType reflected = reflector * scaled; VERIFY_IS_APPROX(reflected[0], beta / scale); VERIFY_IS_MUCH_SMALLER_THAN(reflected.tail(size - 1).cwiseAbs().maxCoeff(), RealScalar(1)); } template void householder_overflow_cases() { typedef Matrix VectorType; const RealScalar huge = numext::sqrt(NumTraits::highest()) * RealScalar(4); // The head alone squares out of range. VectorType head(2); head << huge, huge / RealScalar(8); householder_overflow(head); // A tail component alone squares out of range, with a zero head. VectorType tail(3); tail << RealScalar(0), huge * RealScalar(0.6), huge * RealScalar(0.8); householder_overflow(tail); // Accumulated overflow: every square is representable, their sum is not. householder_overflow(VectorType::Constant(65, huge / RealScalar(4))); // A QR factorization of a matrix at this scale has to compose from those reflectors. const Index rows = 6, cols = 4; Matrix matrix(rows, cols); for (Index j = 0; j < cols; ++j) for (Index i = 0; i < rows; ++i) matrix(i, j) = huge * RealScalar(internal::random(-1.0, 1.0)); const HouseholderQR > qr(matrix); const Matrix q = qr.householderQ() * Matrix::Identity(rows, cols); const Matrix r = qr.matrixQR().topRows(cols).template triangularView().toDenseMatrix(); const Matrix scaledMatrix = matrix / huge; VERIFY_IS_APPROX((q * r) / huge, scaledMatrix); } EIGEN_DECLARE_TEST(householder_fastmath) { CALL_SUBTEST_1(householder_overflow_cases()); CALL_SUBTEST_2(householder_overflow_cases()); }