Cloned SEACAS for EXODUS library with extra build files for internal package management.
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.

241 lines
6.1 KiB

2 years ago
// 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);
}