// Copyright(C) 1999-2020 National Technology & Engineering Solutions // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with // NTESS, the U.S. Government retains certain rights in this software. // // See packages/seacas/LICENSE for details #include "Tolerance.h" #include // for abs #include // for int32_t, int64_t namespace { /* See http://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format for the potential portability problems with the union and bit-fields below. */ union Float_t { explicit Float_t(float num = 0.0F) : f(num) {} // Portable extraction of components. bool Negative() const { return (static_cast(i) >> 31) != 0; } int32_t i; float f; }; union Double_t { explicit Double_t(double num = 0.0) : f(num) {} // Portable extraction of components. bool Negative() const { return (static_cast(i) >> 63) != 0; } int64_t i; double f; }; bool AlmostEqualUlpsFloat(float A, float B, int maxUlpsDiff) { Float_t uA(A); Float_t uB(B); // Different signs means they do not match. if (uA.Negative() != uB.Negative()) { // Check for equality to make sure +0==-0 return (A == B); } // Find the difference in ULPs. int ulpsDiff = std::abs(uA.i - uB.i); return (ulpsDiff <= maxUlpsDiff); } bool AlmostEqualUlpsDouble(double A, double B, int maxUlpsDiff) { Double_t uA(A); Double_t uB(B); // Different signs means they do not match. if (uA.Negative() != uB.Negative()) { // Check for equality to make sure +0==-0 return (A == B); } // Find the difference in ULPs. int ulpsDiff = std::abs(uA.i - uB.i); return (ulpsDiff <= maxUlpsDiff); } } // namespace bool Tolerance::use_old_floor = false; bool Tolerance::Diff(double v1, double v2) const { if (type == ToleranceMode::IGNORE_) { return false; } if (use_old_floor) { if (fabs(v1 - v2) < floor) { return false; } } else { if (fabs(v1) <= floor && fabs(v2) <= floor) { return false; } } if (type == ToleranceMode::RELATIVE_) { if (v1 == 0.0 && v2 == 0.0) { return false; } double max = fabs(v1) < fabs(v2) ? fabs(v2) : fabs(v1); return fabs(v1 - v2) > value * max; } if (type == ToleranceMode::ABSOLUTE_) { return fabs(v1 - v2) > value; } if (type == ToleranceMode::COMBINED_) { // if (Abs(x - y) <= Max(absTol, relTol * Max(Abs(x), Abs(y)))) // In the current implementation, absTol == relTol; // At some point, store both values... // In summary, use abs tolerance if both values are less than 1.0; // else use relative tolerance. double max = fabs(v1) < fabs(v2) ? fabs(v2) : fabs(v1); double tol = 1.0 < max ? max : 1.0; return fabs(v1 - v2) >= tol * value; // EIGEN type diffs work on the absolute values. They are intended // for diffing eigenvectors where one shape may be the inverse of // the other and they should compare equal. Eventually would like // to do a better check to ensure that ratio of one shape to other // is 1 or -1... } if (type == ToleranceMode::ULPS_FLOAT_) { return !AlmostEqualUlpsFloat(v1, v2, static_cast(value)); } if (type == ToleranceMode::ULPS_DOUBLE_) { return !AlmostEqualUlpsDouble(v1, v2, static_cast(value)); } if (type == ToleranceMode::EIGEN_REL_) { if (v1 == 0.0 && v2 == 0.0) { return false; } double max = fabs(v1) < fabs(v2) ? fabs(v2) : fabs(v1); return fabs(fabs(v1) - fabs(v2)) > value * max; } else if (type == ToleranceMode::EIGEN_ABS_) { return fabs(fabs(v1) - fabs(v2)) > value; } else if (type == ToleranceMode::EIGEN_COM_) { // if (Abs(x - y) <= Max(absTol, relTol * Max(Abs(x), Abs(y)))) // In the current implementation, absTol == relTol; // At some point, store both values... // In summary, use abs tolerance if both values are less than 1.0; // else use relative tolerance. double max = fabs(v1) < fabs(v2) ? fabs(v2) : fabs(v1); double tol = 1.0 < max ? max : 1.0; return fabs(fabs(v1) - fabs(v2)) >= tol * value; } else { return true; } } const char *Tolerance::typestr() const { if (type == ToleranceMode::RELATIVE_) { return "relative"; } if (type == ToleranceMode::ABSOLUTE_) { return "absolute"; } if (type == ToleranceMode::COMBINED_) { return "combined"; } if (type == ToleranceMode::ULPS_FLOAT_) { return "ulps_float"; } else if (type == ToleranceMode::ULPS_DOUBLE_) { return "ulps_double"; } else if (type == ToleranceMode::EIGEN_REL_) { return "eigenrel"; } else if (type == ToleranceMode::EIGEN_ABS_) { return "eigenabs"; } else if (type == ToleranceMode::EIGEN_COM_) { return "eigencom"; } else { return "ignore"; } } const char *Tolerance::abrstr() const { if (type == ToleranceMode::RELATIVE_) { return "rel"; } if (type == ToleranceMode::ABSOLUTE_) { return "abs"; } if (type == ToleranceMode::COMBINED_) { return "com"; } if (type == ToleranceMode::ULPS_FLOAT_) { return "upf"; } else if (type == ToleranceMode::ULPS_DOUBLE_) { return "upd"; } else if (type == ToleranceMode::EIGEN_REL_) { return "ere"; } else if (type == ToleranceMode::EIGEN_ABS_) { return "eab"; } else if (type == ToleranceMode::EIGEN_COM_) { return "eco"; } else { return "ign"; } } double Tolerance::UlpsDiffFloat(double A, double B) const { Float_t uA(A); Float_t uB(B); // Different signs means they do not match. if (uA.Negative() != uB.Negative()) { // Check for equality to make sure +0==-0 if (A == B) { return 0.0; } return 2 << 28; } // Find the difference in ULPs. return abs(uA.i - uB.i); } double Tolerance::UlpsDiffDouble(double A, double B) const { Double_t uA(A); Double_t uB(B); // Different signs means they do not match. if (uA.Negative() != uB.Negative()) { // Check for equality to make sure +0==-0 if (A == B) { return 0.0; } return 2 << 28; } // Find the difference in ULPs. return std::abs(uA.i - uB.i); }