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.
132 lines
3.4 KiB
132 lines
3.4 KiB
// 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;
|
|
}
|
|
|