| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> | |
| // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #ifndef EIGEN_APPROX_COMPARISONS_H | |
| #define EIGEN_APPROX_COMPARISONS_H | |
| // IWYU pragma: private | |
| #include "./InternalHeaderCheck.h" | |
| namespace Eigen { | |
| namespace internal { | |
| // Keep custom scalars on the algebraic path: exponent scaling requires binary floating-point arithmetic. | |
| template <typename Scalar> | |
| struct use_scaled_comparison | |
| : bool_constant<(std::is_floating_point<Scalar>::value && std::numeric_limits<Scalar>::radix == 2) || | |
| std::is_same<Scalar, half>::value || std::is_same<Scalar, bfloat16>::value> {}; | |
| template <typename RealScalar> | |
| struct use_scaled_comparison<std::complex<RealScalar>> : use_scaled_comparison<RealScalar> {}; | |
| // A nonnegative magnitude fraction * 2^exponent, including norms larger than the scalar range. Positive finite | |
| // magnitudes order lexicographically by (exponent, fraction), the member order. | |
| template <typename RealScalar> | |
| struct comparison_magnitude { | |
| int exponent = 0; | |
| RealScalar fraction; | |
| EIGEN_DEVICE_FUNC explicit comparison_magnitude(const RealScalar& value) : fraction(value) { | |
| if (value > RealScalar(0) && value <= NumTraits<RealScalar>::highest()) { | |
| EIGEN_USING_STD(frexp); | |
| fraction = frexp(value, &exponent); | |
| } | |
| } | |
| template <typename OtherRealScalar> | |
| EIGEN_DEVICE_FUNC explicit comparison_magnitude(const comparison_magnitude<OtherRealScalar>& value) | |
| : exponent(value.exponent), fraction(RealScalar(value.fraction)) {} | |
| EIGEN_DEVICE_FUNC void multiply(const RealScalar& value) { | |
| const comparison_magnitude factor(value); | |
| const comparison_magnitude product(fraction * factor.fraction); | |
| fraction = product.fraction; | |
| exponent += factor.exponent + product.exponent; | |
| } | |
| EIGEN_DEVICE_FUNC bool isFinite() const { return fraction <= RealScalar(1); } | |
| }; | |
| template <typename RealScalar> | |
| EIGEN_DEVICE_FUNC bool operator<=(const comparison_magnitude<RealScalar>& x, | |
| const comparison_magnitude<RealScalar>& y) { | |
| if (!x.isFinite()) return false; | |
| // Zero and non-finite magnitudes carry no exponent. | |
| if (!y.isFinite() || x.fraction == RealScalar(0) || y.fraction == RealScalar(0)) return x.fraction <= y.fraction; | |
| return x.exponent < y.exponent || (x.exponent == y.exponent && x.fraction <= y.fraction); | |
| } | |
| template <typename RealScalar, typename OtherRealScalar> | |
| EIGEN_DEVICE_FUNC bool operator<=(const comparison_magnitude<RealScalar>& x, | |
| const comparison_magnitude<OtherRealScalar>& y) { | |
| using Common = std::common_type_t<RealScalar, OtherRealScalar>; | |
| return comparison_magnitude<Common>(x) <= comparison_magnitude<Common>(y); | |
| } | |
| // Types whose data(), rowStride() and colStride() address every coefficient. DirectAccessBit alone does not promise | |
| // this: a single-row IndexedView of a column-major matrix and the RealView of a strided complex block report strides | |
| // that do not. | |
| template <typename X> | |
| struct scaled_comparison_viewable : std::false_type {}; | |
| template <typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols> | |
| struct scaled_comparison_viewable<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>> : std::true_type {}; | |
| template <typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols> | |
| struct scaled_comparison_viewable<Array<Scalar, Rows, Cols, Options, MaxRows, MaxCols>> : std::true_type {}; | |
| template <typename PlainObjectType, int Options, typename StrideType> | |
| struct scaled_comparison_viewable<Map<PlainObjectType, Options, StrideType>> : std::true_type {}; | |
| template <typename PlainObjectType, int Options, typename StrideType> | |
| struct scaled_comparison_viewable<Ref<PlainObjectType, Options, StrideType>> : std::true_type {}; | |
| template <typename XprType, int BlockRows, int BlockCols, bool InnerPanel> | |
| struct scaled_comparison_viewable<Block<XprType, BlockRows, BlockCols, InnerPanel>> | |
| : scaled_comparison_viewable<std::remove_const_t<XprType>> {}; | |
| template <typename XprType> | |
| struct scaled_comparison_viewable<Transpose<XprType>> : scaled_comparison_viewable<std::remove_const_t<XprType>> {}; | |
| template <typename XprType> | |
| struct scaled_comparison_viewable<ArrayWrapper<XprType>> : scaled_comparison_viewable<std::remove_const_t<XprType>> {}; | |
| template <typename XprType> | |
| struct scaled_comparison_viewable<MatrixWrapper<XprType>> : scaled_comparison_viewable<std::remove_const_t<XprType>> {}; | |
| // The scaled path reduces each operand several times, so it runs on at most three view types per scalar type rather | |
| // than being instantiated for every operand expression. A viewable operand with direct access is viewed in place, | |
| // keeping packet access when its inner stride is one; any other operand, including a lazy expression or a shape that | |
| // hides operator*(Scalar) as Homogeneous does, is evaluated first, which allocates only for dynamic sizes and for | |
| // fixed sizes above EIGEN_STACK_ALLOCATION_LIMIT. | |
| template <typename X, bool DirectAccess = has_direct_access<X>::value && scaled_comparison_viewable<X>::value, | |
| bool UnitInnerStride = inner_stride_at_compile_time<X>::value == 1> | |
| struct scaled_comparison_operand { | |
| using View = | |
| Map<const Matrix<typename X::Scalar, Dynamic, Dynamic, X::IsRowMajor ? RowMajor : ColMajor>, 0, OuterStride<>>; | |
| EIGEN_DEVICE_FUNC explicit scaled_comparison_operand(const X& x) | |
| : view(x.data(), x.rows(), x.cols(), OuterStride<>(x.outerStride())) {} | |
| View view; | |
| }; | |
| template <typename X> | |
| struct scaled_comparison_operand<X, true, false> { | |
| using View = Map<const Matrix<typename X::Scalar, Dynamic, Dynamic>, 0, Stride<Dynamic, Dynamic>>; | |
| EIGEN_DEVICE_FUNC explicit scaled_comparison_operand(const X& x) | |
| : view(x.data(), x.rows(), x.cols(), Stride<Dynamic, Dynamic>(x.colStride(), x.rowStride())) {} | |
| View view; | |
| }; | |
| template <typename X, bool UnitInnerStride> | |
| struct scaled_comparison_operand<X, false, UnitInnerStride> { | |
| // A fixed capacity the stack limit would reject makes the comparison fail to compile for an operand the caller | |
| // never stores, so the temporary takes it only where DenseStorage accepts it. | |
| static constexpr bool FixedCapacity = | |
| EIGEN_STACK_ALLOCATION_LIMIT == 0 || | |
| (X::MaxSizeAtCompileTime != Dynamic && | |
| std::ptrdiff_t(X::MaxSizeAtCompileTime) * std::ptrdiff_t(sizeof(typename X::Scalar)) <= | |
| std::ptrdiff_t(EIGEN_STACK_ALLOCATION_LIMIT)); | |
| // Column-major unless it holds at most one row, where Matrix requires row-major storage. | |
| using Plain = | |
| Matrix<typename X::Scalar, Dynamic, Dynamic, | |
| X::MaxRowsAtCompileTime == 1 && X::MaxColsAtCompileTime != 1 ? RowMajor : ColMajor, | |
| FixedCapacity ? X::MaxRowsAtCompileTime : Dynamic, FixedCapacity ? X::MaxColsAtCompileTime : Dynamic>; | |
| using View = typename scaled_comparison_operand<Plain>::View; | |
| EIGEN_DEVICE_FUNC explicit scaled_comparison_operand(const X& x) | |
| : value(x.matrix()), view(scaled_comparison_operand<Plain>(value).view) {} | |
| Plain value; | |
| View view; | |
| }; | |
| template <typename Components> | |
| EIGEN_DEVICE_FUNC typename Components::Scalar scaled_comparison_max_coeff(const Components& components) { | |
| using RealScalar = typename Components::Scalar; | |
| if (components.size() == 0) return RealScalar(0); | |
| return safe_scaling<RealScalar>::recover_flushed_max_coeff(components, | |
| components.cwiseAbs().template maxCoeff<PropagateNaN>()); | |
| } | |
| template <typename Derived> | |
| EIGEN_DEVICE_FUNC comparison_magnitude<typename stable_norm_accumulator<typename Derived::RealScalar>::type> | |
| scaled_comparison_norm_impl(const MatrixBase<Derived>& matrix) { | |
| using RealScalar = typename stable_norm_accumulator<typename Derived::RealScalar>::type; | |
| const auto& realComponents = matrix.realView(); | |
| const auto& components = realComponents.template cast<RealScalar>(); | |
| const RealScalar scale = scaled_comparison_max_coeff(components); | |
| // Classify first so NaNs do not reach the ordered comparison. | |
| if (!(numext::isfinite)(scale) || !(scale > RealScalar(0))) return comparison_magnitude<RealScalar>(scale); | |
| RealScalar squaredNorm = RealScalar(0); | |
| const auto factors = safe_scaling<RealScalar>::with_scaled( | |
| components, scale, [&](const auto& scaled) { squaredNorm = scaled.squaredNorm(); }); | |
| comparison_magnitude<RealScalar> result(numext::sqrt(squaredNorm)); | |
| result.multiply(factors.scale); | |
| return result; | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC comparison_magnitude<typename stable_norm_accumulator<typename X::RealScalar>::type> | |
| scaled_comparison_distance_impl(const MatrixBase<X>& matrixX, const MatrixBase<Y>& matrixY) { | |
| using Accumulator = typename stable_norm_accumulator<typename X::RealScalar>::type; | |
| using WideScalar = | |
| std::conditional_t<NumTraits<typename X::Scalar>::IsComplex || NumTraits<typename Y::Scalar>::IsComplex, | |
| std::complex<Accumulator>, Accumulator>; | |
| const auto& wideX = matrixX.template cast<WideScalar>(); | |
| const auto& wideY = matrixY.template cast<WideScalar>(); | |
| // FTZ flushes a subnormal difference of normal operands. Scaling a maximum M < 1 up by a power of two first loses | |
| // only differences below M * min; scaling larger operands down could underflow their small components. | |
| const Accumulator maxCoeff = numext::mini( | |
| Accumulator(1), | |
| numext::maxi(scaled_comparison_max_coeff(wideX.realView()), scaled_comparison_max_coeff(wideY.realView()))); | |
| const safe_scaling_factors<Accumulator> factors = supports_power_of_two_scaling<Accumulator>::value | |
| ? safe_scaling<Accumulator>::compute_floor_factors(maxCoeff) | |
| : safe_scaling_factors<Accumulator>(); | |
| // Both calls share one expression type, so scaled_comparison_norm_impl is instantiated once. | |
| auto difference = scaled_comparison_norm_impl(wideX * factors.invScale - wideY * factors.invScale); | |
| if (!difference.isFinite()) { | |
| // Finite operands can overflow on subtraction; halving first keeps every component representable. | |
| const Accumulator halfInvScale = factors.invScale * Accumulator(0.5); | |
| difference = scaled_comparison_norm_impl(wideX * halfInvScale - wideY * halfInvScale); | |
| difference.multiply(Accumulator(2)); | |
| } | |
| difference.multiply(factors.scale); | |
| return difference; | |
| } | |
| template <typename Derived> | |
| EIGEN_DEVICE_FUNC comparison_magnitude<typename stable_norm_accumulator<typename Derived::RealScalar>::type> | |
| scaled_comparison_norm(const Derived& x) { | |
| return scaled_comparison_norm_impl(scaled_comparison_operand<Derived>(x).view); | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC comparison_magnitude<typename stable_norm_accumulator<typename X::RealScalar>::type> | |
| scaled_comparison_distance(const X& x, const Y& y) { | |
| return scaled_comparison_distance_impl(scaled_comparison_operand<X>(x).view, scaled_comparison_operand<Y>(y).view); | |
| } | |
| // Coefficients widened to the stable-norm accumulator, in which ordinary comparisons square and sum. The cast is the | |
| // identity for float and double; half and bfloat16 widen exactly to float, where no half square underflows. | |
| template <typename X> | |
| using approx_comparison_wide_t = | |
| std::conditional_t<NumTraits<typename X::Scalar>::IsComplex, | |
| std::complex<typename stable_norm_accumulator<typename X::RealScalar>::type>, | |
| typename stable_norm_accumulator<typename X::RealScalar>::type>; | |
| template <typename Scalar, bool = use_scaled_comparison<Scalar>::value> | |
| struct approx_comparison_impl { | |
| using RealScalar = typename NumTraits<Scalar>::Real; | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static bool isApprox(const X& x, const Y& y, const RealScalar& prec) { | |
| return (x.matrix() - y.matrix()).cwiseAbs2().sum() <= | |
| prec * prec * numext::mini(x.cwiseAbs2().sum(), y.cwiseAbs2().sum()); | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static bool isMuchSmallerThan(const X& x, const Y& y, const RealScalar& prec) { | |
| return x.cwiseAbs2().sum() <= numext::abs2(prec) * y.cwiseAbs2().sum(); | |
| } | |
| template <typename X> | |
| EIGEN_DEVICE_FUNC static bool isMuchSmallerThan(const X& x, const RealScalar& y, const RealScalar& prec) { | |
| return x.cwiseAbs2().sum() <= numext::abs2(prec * y); | |
| } | |
| }; | |
| template <typename Scalar> | |
| struct approx_comparison_impl<Scalar, true> { | |
| using RealScalar = typename NumTraits<Scalar>::Real; | |
| using Accumulator = typename stable_norm_accumulator<RealScalar>::type; | |
| template <typename Y> | |
| using CommonAccumulator = | |
| std::common_type_t<Accumulator, typename stable_norm_accumulator<typename Y::RealScalar>::type>; | |
| template <typename ValueScalar> | |
| EIGEN_DEVICE_FUNC static typename stable_norm_accumulator<ValueScalar>::type squared_norm_lower_bound(Index size) { | |
| using ValueAccumulator = typename stable_norm_accumulator<ValueScalar>::type; | |
| // Squares accumulate in ValueAccumulator; below n * min / epsilon, flushed squares can affect the comparison. | |
| return stable_normalization_normal_min<ValueAccumulator, ValueAccumulator>::run() / | |
| NumTraits<ValueAccumulator>::epsilon() * ValueAccumulator(size); | |
| } | |
| template <typename BoundScalar> | |
| EIGEN_DEVICE_FUNC static bool safe_squared_norm(const BoundScalar& value, Index size) { | |
| using Common = std::common_type_t<Accumulator, BoundScalar>; | |
| return Common(value) >= Common(squared_norm_lower_bound<RealScalar>(size)) && | |
| Common(value) <= Common(NumTraits<Accumulator>::highest()); | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static bool isApprox(const X& x, const Y& y, const RealScalar& prec) { | |
| // Widening must not admit operands that cannot be subtracted, such as half and float. | |
| EIGEN_CHECK_BINARY_COMPATIBILITY(scalar_difference_op<typename X::Scalar EIGEN_COMMA typename Y::Scalar>, | |
| typename X::Scalar, typename Y::Scalar) | |
| const auto& wideX = x.template cast<approx_comparison_wide_t<X>>(); | |
| const auto& wideY = y.template cast<approx_comparison_wide_t<Y>>(); | |
| const Accumulator x2 = wideX.cwiseAbs2().sum(); | |
| const Accumulator y2 = wideY.cwiseAbs2().sum(); | |
| const Accumulator minimum = numext::mini(x2, y2); | |
| const Accumulator precision2 = Accumulator(prec) * Accumulator(prec); | |
| const Accumulator bound = precision2 * minimum; | |
| // Only the smaller norm enters the bound; overflow of the larger norm is harmless. | |
| if (safe_squared_norm(bound, x.size()) && minimum >= squared_norm_lower_bound<RealScalar>(x.size()) && | |
| precision2 >= squared_norm_lower_bound<RealScalar>(1)) | |
| return (wideX.matrix() - wideY.matrix()).cwiseAbs2().sum() <= bound; | |
| return isApprox_scaled(x, y, prec); | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static bool isMuchSmallerThan(const X& x, const Y& y, const RealScalar& prec) { | |
| using Common = CommonAccumulator<Y>; | |
| typename nested_eval<X, 2>::type nested(x); | |
| typename nested_eval<Y, 2>::type otherNested(y); | |
| const Accumulator x2 = nested.template cast<approx_comparison_wide_t<X>>().cwiseAbs2().sum(); | |
| const auto y2 = otherNested.template cast<approx_comparison_wide_t<Y>>().cwiseAbs2().sum(); | |
| const Accumulator precision2 = numext::abs2(Accumulator(prec)); | |
| const Common bound = Common(precision2) * Common(y2); | |
| // A finite bound above the flushing error makes overflow/underflow of x2 harmless. | |
| if (safe_squared_norm(bound, x.size()) && | |
| Common(y2) >= Common(squared_norm_lower_bound<typename Y::RealScalar>(y.size())) && | |
| precision2 >= squared_norm_lower_bound<RealScalar>(1)) | |
| return Common(x2) <= bound; | |
| return isMuchSmallerThan_scaled(nested, otherNested, prec); | |
| } | |
| template <typename X> | |
| EIGEN_DEVICE_FUNC static bool isMuchSmallerThan(const X& x, const RealScalar& y, const RealScalar& prec) { | |
| typename nested_eval<X, 2>::type nested(x); | |
| const Accumulator x2 = nested.template cast<approx_comparison_wide_t<X>>().cwiseAbs2().sum(); | |
| const Accumulator bound = numext::abs2(Accumulator(prec) * Accumulator(y)); | |
| if (safe_squared_norm(bound, x.size())) return x2 <= bound; | |
| return isMuchSmallerThan_scaled(nested, y, prec); | |
| } | |
| private: | |
| // Keep exponent scaling from inhibiting inlining of ordinary comparisons. | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static EIGEN_DONT_INLINE bool isApprox_scaled(const X& xExpr, const Y& yExpr, | |
| const RealScalar& prec) { | |
| const scaled_comparison_operand<X> x(xExpr); | |
| const scaled_comparison_operand<Y> y(yExpr); | |
| const auto nx = scaled_comparison_norm_impl(x.view); | |
| const auto ny = scaled_comparison_norm_impl(y.view); | |
| if (!nx.isFinite() || !ny.isFinite()) return false; | |
| auto tolerance = nx <= ny ? nx : ny; | |
| tolerance.multiply(numext::abs(Accumulator(prec))); | |
| return scaled_comparison_distance_impl(x.view, y.view) <= tolerance; | |
| } | |
| template <typename X, typename Y> | |
| EIGEN_DEVICE_FUNC static EIGEN_DONT_INLINE bool isMuchSmallerThan_scaled(const X& x, const Y& y, | |
| const RealScalar& prec) { | |
| using Common = CommonAccumulator<Y>; | |
| comparison_magnitude<Common> tolerance(scaled_comparison_norm(y)); | |
| tolerance.multiply(numext::abs(Common(prec))); | |
| return scaled_comparison_norm(x) <= tolerance; | |
| } | |
| template <typename X> | |
| EIGEN_DEVICE_FUNC static EIGEN_DONT_INLINE bool isMuchSmallerThan_scaled(const X& x, const RealScalar& y, | |
| const RealScalar& prec) { | |
| comparison_magnitude<Accumulator> tolerance(numext::abs(Accumulator(y))); | |
| tolerance.multiply(numext::abs(Accumulator(prec))); | |
| return scaled_comparison_norm(x) <= tolerance; | |
| } | |
| }; | |
| // Exponent scaling needs binary floating-point on both sides. | |
| template <typename Derived, typename OtherDerived> | |
| using approx_comparison_impl_t = | |
| approx_comparison_impl<typename Derived::Scalar, use_scaled_comparison<typename Derived::Scalar>::value && | |
| use_scaled_comparison<typename OtherDerived::Scalar>::value>; | |
| template <typename Derived, typename OtherDerived, bool is_integer = NumTraits<typename Derived::Scalar>::IsInteger> | |
| struct isApprox_selector { | |
| EIGEN_DEVICE_FUNC static bool run(const Derived& x, const OtherDerived& y, const typename Derived::RealScalar& prec) { | |
| typename internal::nested_eval<Derived, 2>::type nested(x); | |
| typename internal::nested_eval<OtherDerived, 2>::type otherNested(y); | |
| return approx_comparison_impl_t<Derived, OtherDerived>::isApprox(nested, otherNested, prec); | |
| } | |
| }; | |
| template <typename Derived, typename OtherDerived> | |
| struct isApprox_selector<Derived, OtherDerived, true> { | |
| EIGEN_DEVICE_FUNC static bool run(const Derived& x, const OtherDerived& y, const typename Derived::RealScalar&) { | |
| return x.matrix() == y.matrix(); | |
| } | |
| }; | |
| template <typename Derived, typename OtherDerived, bool is_integer = NumTraits<typename Derived::Scalar>::IsInteger> | |
| struct isMuchSmallerThan_object_selector { | |
| EIGEN_DEVICE_FUNC static bool run(const Derived& x, const OtherDerived& y, const typename Derived::RealScalar& prec) { | |
| return approx_comparison_impl_t<Derived, OtherDerived>::isMuchSmallerThan(x, y, prec); | |
| } | |
| }; | |
| template <typename Derived, typename OtherDerived> | |
| struct isMuchSmallerThan_object_selector<Derived, OtherDerived, true> { | |
| EIGEN_DEVICE_FUNC static bool run(const Derived& x, const OtherDerived&, const typename Derived::RealScalar&) { | |
| return x.matrix() == Derived::Zero(x.rows(), x.cols()).matrix(); | |
| } | |
| }; | |
| template <typename Derived, bool is_integer = NumTraits<typename Derived::Scalar>::IsInteger> | |
| struct isMuchSmallerThan_scalar_selector { | |
| EIGEN_DEVICE_FUNC static bool run(const Derived& x, const typename Derived::RealScalar& y, | |
| const typename Derived::RealScalar& prec) { | |
| return approx_comparison_impl<typename Derived::Scalar>::isMuchSmallerThan(x, y, prec); | |
| } | |
| }; | |
| template <typename Derived> | |
| struct isMuchSmallerThan_scalar_selector<Derived, true> | |
| : isMuchSmallerThan_object_selector<Derived, typename Derived::RealScalar, true> {}; | |
| } // end namespace internal | |
| /** \returns \c true if \c *this is approximately equal to \a other, within the precision | |
| * determined by \a prec. | |
| * | |
| * \note This is a relative norm comparison. Two vectors \f$ v \f$ and \f$ w \f$ | |
| * are considered to be approximately equal within precision \f$ p \f$ if | |
| * \f[ \Vert v - w \Vert \leqslant p\,\min(\Vert v\Vert, \Vert w\Vert). \f] | |
| * For matrices, the comparison is done using the Hilbert-Schmidt norm (aka Frobenius norm | |
| * L2 norm). | |
| * | |
| * \a prec is a relative tolerance chosen by the caller, not a bound on floating-point rounding error. | |
| * The default, NumTraits<Scalar>::dummy_precision(), does not account for the operation, dimensions, | |
| * or conditioning of the problem. Choose an explicit tolerance when checking numerical accuracy. | |
| * A norm comparison also does not bound the relative error of each coefficient. | |
| * | |
| * \note Because of the multiplicativeness of this comparison, one can't use this function | |
| * to check whether \c *this is approximately equal to the zero matrix or vector. | |
| * Indeed, \c isApprox(zero) returns false unless \c *this itself is exactly the zero matrix | |
| * or vector. If you want to test whether \c *this is small relative to a reference norm, | |
| * use isMuchSmallerThan() instead. | |
| * | |
| * \sa isMuchSmallerThan(), isZero() | |
| */ | |
| template <typename Derived> | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC constexpr bool DenseBase<Derived>::isApprox(const DenseBase<OtherDerived>& other, | |
| const RealScalar& prec) const { | |
| return internal::isApprox_selector<Derived, OtherDerived>::run(derived(), other.derived(), prec); | |
| } | |
| /** \returns \c true if the norm of \c *this is much smaller than \a other, | |
| * within the precision determined by \a prec. | |
| * | |
| * \note This is a relative norm comparison. A vector \f$ v \f$ is | |
| * considered to be much smaller than \f$ x \f$ within precision \f$ p \f$ if | |
| * \f[ \Vert v \Vert \leqslant p\,\vert x\vert. \f] | |
| * | |
| * For matrices, the comparison is done using the Hilbert-Schmidt norm. For this reason, | |
| * the value of the reference scalar \a other should come from the Hilbert-Schmidt norm | |
| * of a reference matrix of same dimensions. | |
| * | |
| * \sa isApprox(), isMuchSmallerThan(const DenseBase<OtherDerived>&, RealScalar) const | |
| */ | |
| template <typename Derived> | |
| EIGEN_DEVICE_FUNC constexpr bool DenseBase<Derived>::isMuchSmallerThan(const typename NumTraits<Scalar>::Real& other, | |
| const RealScalar& prec) const { | |
| return internal::isMuchSmallerThan_scalar_selector<Derived>::run(derived(), other, prec); | |
| } | |
| /** \returns \c true if the norm of \c *this is much smaller than the norm of \a other, | |
| * within the precision determined by \a prec. | |
| * | |
| * \note This is a relative norm comparison. A vector \f$ v \f$ is | |
| * considered to be much smaller than a vector \f$ w \f$ within precision \f$ p \f$ if | |
| * \f[ \Vert v \Vert \leqslant p\,\Vert w\Vert. \f] | |
| * For matrices, the comparison is done using the Hilbert-Schmidt norm. | |
| * | |
| * \sa isApprox(), isMuchSmallerThan(const RealScalar&, RealScalar) const | |
| */ | |
| template <typename Derived> | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC constexpr bool DenseBase<Derived>::isMuchSmallerThan(const DenseBase<OtherDerived>& other, | |
| const RealScalar& prec) const { | |
| return internal::isMuchSmallerThan_object_selector<Derived, OtherDerived>::run(derived(), other.derived(), prec); | |
| } | |
| } // end namespace Eigen | |
| #endif // EIGEN_APPROX_COMPARISONS_H |