You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
6.1 KiB
240 lines
6.1 KiB
// 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 <cstdlib> // for abs
|
|
#include <sys/types.h> // 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<uint32_t>(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<uint64_t>(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<int>(value));
|
|
}
|
|
if (type == ToleranceMode::ULPS_DOUBLE_) {
|
|
return !AlmostEqualUlpsDouble(v1, v2, static_cast<int>(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);
|
|
}
|
|
|