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.

133 lines
3.4 KiB

2 years ago
// Copyright(C) 1999-2020, 2022 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
#pragma once
#include "map.h"
#include <cmath>
// See http://realtimecollisiondetection.net/blog/?p=89 for a
// description of the COMBINED tolerance. Basically:
// The absolute tolerance test fails when x and y become large, and
// the relative tolerance test fails when they become small. It is
// therefore desired to combine these two tests together in a single
// test. Over the years at GDC, as well as in my book, I've
// suggested the following combined tolerance test:
//
// if (Abs(x - y) <= EPSILON * Max(1.0f, Abs(x), Abs(y)) ...
enum class ToleranceMode {
RELATIVE_ = 0,
ABSOLUTE_ = 1,
COMBINED_ = 2,
IGNORE_ = 3,
EIGEN_REL_ = 4,
EIGEN_ABS_ = 5,
EIGEN_COM_ = 6,
ULPS_FLOAT_ = 7,
ULPS_DOUBLE_ = 8
};
class Tolerance
{
public:
Tolerance() = default;
Tolerance(ToleranceMode tol_type, double tol_value, double tol_floor)
: type(tol_type), value(tol_value), floor(tol_floor)
{
}
// Default copy constructor and operator= should work for this simple class...
bool Diff(double v1, double v2) const;
double Delta(double v1, double v2) const;
const char *typestr() const;
const char *abrstr() const;
ToleranceMode type{ToleranceMode::RELATIVE_};
double value{0.0};
double floor{0.0};
// If true, use the older definition of the floor tolerance which was
// |a-b| < floor. The new definition is |a| < floor && |b| < floor
static bool use_old_floor;
private:
double UlpsDiffFloat(double A, double B) const;
double UlpsDiffDouble(double A, double B) const;
};
inline double Tolerance::Delta(double v1, double v2) const
{
if (type == ToleranceMode::IGNORE_) {
return 0.0;
}
double fabv1 = std::fabs(v1);
double fabv2 = std::fabs(v2);
bool diff = false;
if (!use_old_floor) {
if (fabv1 >= floor || fabv2 >= floor) {
diff = true;
}
}
else {
if (std::fabs(v1 - v2) >= floor) {
diff = true;
}
}
if (diff) {
if (type == ToleranceMode::RELATIVE_) {
if (v1 == 0.0 && v2 == 0.0) {
return 0.0;
}
double max = fabv1 < fabv2 ? fabv2 : fabv1;
return std::fabs(v1 - v2) / max;
}
if (type == ToleranceMode::ABSOLUTE_) {
return std::fabs(v1 - v2);
}
else if (type == ToleranceMode::COMBINED_) {
double max = fabv1 < fabv2 ? fabv2 : fabv1;
if (max > 1.0) {
return std::fabs(v1 - v2) / max;
}
else {
return std::fabs(v1 - v2);
}
}
else if (type == ToleranceMode::ULPS_FLOAT_) {
return UlpsDiffFloat(v1, v2);
}
else if (type == ToleranceMode::ULPS_DOUBLE_) {
return UlpsDiffDouble(v1, v2);
}
else if (type == ToleranceMode::EIGEN_REL_) {
if (v1 == 0.0 && v2 == 0.0) {
return 0.0;
}
double max = fabv1 < fabv2 ? fabv2 : fabv1;
return std::fabs(fabv1 - fabv2) / max;
}
else if (type == ToleranceMode::EIGEN_ABS_) {
return std::fabs(fabv1 - fabv2);
}
else if (type == ToleranceMode::EIGEN_COM_) {
double max = fabv1 < fabv2 ? fabv2 : fabv1;
if (max > 1.0) {
return std::fabs(fabv1 - fabv2) / max;
}
else {
return std::fabs(fabv1 - fabv2);
}
}
}
return 0.0;
}