forked from organicmaps/organicmaps
[base] Fixed AlmostEqualULPs in case of possible int overflow.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
This commit is contained in:
parent
8b6593a637
commit
b8d6fb09a7
6 changed files with 84 additions and 36 deletions
|
@ -42,6 +42,7 @@ set(
|
||||||
lower_case.cpp
|
lower_case.cpp
|
||||||
lru_cache.hpp
|
lru_cache.hpp
|
||||||
macros.hpp
|
macros.hpp
|
||||||
|
math.cpp
|
||||||
math.hpp
|
math.hpp
|
||||||
matrix.hpp
|
matrix.hpp
|
||||||
mem_trie.hpp
|
mem_trie.hpp
|
||||||
|
|
|
@ -46,7 +46,7 @@ UNIT_TEST(PowUInt)
|
||||||
TEST_EQUAL(base::PowUint(3, 10), 59049, ());
|
TEST_EQUAL(base::PowUint(3, 10), 59049, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
UNIT_TEST(AlmostEqualULPs_Smoke)
|
UNIT_TEST(AlmostEqualULPs_double)
|
||||||
{
|
{
|
||||||
TEST_ALMOST_EQUAL_ULPS(3.0, 3.0, ());
|
TEST_ALMOST_EQUAL_ULPS(3.0, 3.0, ());
|
||||||
TEST_ALMOST_EQUAL_ULPS(+0.0, -0.0, ());
|
TEST_ALMOST_EQUAL_ULPS(+0.0, -0.0, ());
|
||||||
|
@ -70,6 +70,30 @@ UNIT_TEST(AlmostEqualULPs_Smoke)
|
||||||
TEST(!base::AlmostEqualULPs(0.0, eps), ());
|
TEST(!base::AlmostEqualULPs(0.0, eps), ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNIT_TEST(AlmostEqualULPs_float)
|
||||||
|
{
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(3.0f, 3.0f, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(+0.0f, -0.0f, ());
|
||||||
|
|
||||||
|
float const eps = std::numeric_limits<float>::epsilon();
|
||||||
|
float const dmax = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(1.0f + eps, 1.0f, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f + eps, ());
|
||||||
|
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(dmax, dmax, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(-dmax, -dmax, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(dmax/2.0f, dmax/2.0f, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(1.0f/dmax, 1.0f/dmax, ());
|
||||||
|
TEST_ALMOST_EQUAL_ULPS(-1.0f/dmax, -1.0f/dmax, ());
|
||||||
|
|
||||||
|
TEST(!base::AlmostEqualULPs(1.0f, -1.0f), ());
|
||||||
|
TEST(!base::AlmostEqualULPs(2.0f, -2.0f), ());
|
||||||
|
TEST(!base::AlmostEqualULPs(dmax, -dmax), ());
|
||||||
|
TEST(!base::AlmostEqualULPs(0.0f, eps), ());
|
||||||
|
}
|
||||||
|
|
||||||
UNIT_TEST(AlmostEqual_Smoke)
|
UNIT_TEST(AlmostEqual_Smoke)
|
||||||
{
|
{
|
||||||
double const small = 1e-18;
|
double const small = 1e-18;
|
||||||
|
|
51
base/math.cpp
Normal file
51
base/math.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "math.hpp"
|
||||||
|
|
||||||
|
#include <boost/integer.hpp>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace base
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Float>
|
||||||
|
bool AlmostEqualULPs(Float x, Float y, uint32_t maxULPs)
|
||||||
|
{
|
||||||
|
static_assert(std::is_floating_point<Float>::value, "");
|
||||||
|
static_assert(std::numeric_limits<Float>::is_iec559, "");
|
||||||
|
|
||||||
|
// Make sure maxUlps is non-negative and small enough that the
|
||||||
|
// default NaN won't compare as equal to anything.
|
||||||
|
ASSERT_LESS(maxULPs, 4 * 1024 * 1024, ());
|
||||||
|
|
||||||
|
int constexpr bits = CHAR_BIT * sizeof(Float);
|
||||||
|
typedef typename boost::int_t<bits>::exact IntType;
|
||||||
|
typedef typename boost::uint_t<bits>::exact UIntType;
|
||||||
|
|
||||||
|
// Same as *reinterpret_cast<IntType const *>(&x), but without warnings.
|
||||||
|
IntType xInt, yInt;
|
||||||
|
static_assert(sizeof(xInt) == sizeof(x), "bit_cast impossible");
|
||||||
|
std::memcpy(&xInt, &x, sizeof(x));
|
||||||
|
std::memcpy(&yInt, &y, sizeof(y));
|
||||||
|
|
||||||
|
// Make xInt and yInt lexicographically ordered as a twos-complement int.
|
||||||
|
IntType const highestBit = IntType(1) << (bits - 1);
|
||||||
|
if (xInt < 0)
|
||||||
|
xInt = highestBit - xInt;
|
||||||
|
if (yInt < 0)
|
||||||
|
yInt = highestBit - yInt;
|
||||||
|
|
||||||
|
// Calculate diff with special case to avoid IntType overflow.
|
||||||
|
UIntType diff;
|
||||||
|
if ((xInt >= 0) == (yInt >= 0))
|
||||||
|
diff = Abs(xInt - yInt);
|
||||||
|
else
|
||||||
|
diff = UIntType(Abs(xInt)) + UIntType(Abs(yInt));
|
||||||
|
|
||||||
|
return diff <= maxULPs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template bool AlmostEqualULPs<float>(float x, float y, uint32_t maxULPs);
|
||||||
|
template bool AlmostEqualULPs<double>(double x, double y, uint32_t maxULPs);
|
||||||
|
|
||||||
|
} // namespace base
|
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
#include "base/assert.hpp"
|
#include "base/assert.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm> // std::max
|
||||||
#include <climits>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <functional> // std::hash
|
||||||
#include <functional>
|
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <boost/integer.hpp>
|
|
||||||
|
|
||||||
namespace math
|
namespace math
|
||||||
{
|
{
|
||||||
double constexpr pi = 3.14159265358979323846;
|
double constexpr pi = 3.14159265358979323846;
|
||||||
|
@ -44,33 +39,7 @@ int constexpr Sign(Number const number) noexcept
|
||||||
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
||||||
// for details.
|
// for details.
|
||||||
template <typename Float>
|
template <typename Float>
|
||||||
bool AlmostEqualULPs(Float x, Float y, unsigned int maxULPs = 256)
|
bool AlmostEqualULPs(Float x, Float y, uint32_t maxULPs = 256);
|
||||||
{
|
|
||||||
static_assert(std::is_floating_point<Float>::value, "");
|
|
||||||
static_assert(std::numeric_limits<Float>::is_iec559, "");
|
|
||||||
|
|
||||||
// Make sure maxUlps is non-negative and small enough that the
|
|
||||||
// default NaN won't compare as equal to anything.
|
|
||||||
ASSERT_LESS(maxULPs, 4 * 1024 * 1024, ());
|
|
||||||
|
|
||||||
int const bits = CHAR_BIT * sizeof(Float);
|
|
||||||
typedef typename boost::int_t<bits>::exact IntType;
|
|
||||||
typedef typename boost::uint_t<bits>::exact UIntType;
|
|
||||||
IntType xInt, yInt;
|
|
||||||
static_assert(sizeof(xInt) == sizeof(x), "bit_cast impossible");
|
|
||||||
std::memcpy(&xInt, &x, sizeof(x));
|
|
||||||
std::memcpy(&yInt, &y, sizeof(y));
|
|
||||||
// Make xInt and yInt lexicographically ordered as a twos-complement int
|
|
||||||
IntType const highestBit = IntType(1) << (bits - 1);
|
|
||||||
if (xInt < 0)
|
|
||||||
xInt = highestBit - xInt;
|
|
||||||
if (yInt < 0)
|
|
||||||
yInt = highestBit - yInt;
|
|
||||||
|
|
||||||
UIntType const diff = Abs(xInt - yInt);
|
|
||||||
|
|
||||||
return diff <= maxULPs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if x and y are equal up to the absolute difference eps.
|
// Returns true if x and y are equal up to the absolute difference eps.
|
||||||
// Does not produce a sensible result if any of the arguments is NaN or infinity.
|
// Does not produce a sensible result if any of the arguments is NaN or infinity.
|
||||||
|
|
|
@ -28,7 +28,6 @@ UNIT_TEST(ReadFeatures_Smoke)
|
||||||
LOG(LINFO, (guard.GetNumFeatures()));
|
LOG(LINFO, (guard.GetNumFeatures()));
|
||||||
for (uint32_t i = 0; i + 1 < guard.GetNumFeatures(); ++i)
|
for (uint32_t i = 0; i + 1 < guard.GetNumFeatures(); ++i)
|
||||||
{
|
{
|
||||||
LOG(LINFO, ("Trying", i, i + 1));
|
|
||||||
auto ft1 = guard.GetFeatureByIndex(i);
|
auto ft1 = guard.GetFeatureByIndex(i);
|
||||||
auto ft2 = guard.GetFeatureByIndex(i + 1);
|
auto ft2 = guard.GetFeatureByIndex(i + 1);
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,7 @@
|
||||||
67A609AF1C88642E001E641A /* deferred_task.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67A609AD1C88642E001E641A /* deferred_task.hpp */; };
|
67A609AF1C88642E001E641A /* deferred_task.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67A609AD1C88642E001E641A /* deferred_task.hpp */; };
|
||||||
67B52B601AD3C84E00664C17 /* thread_checker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67B52B5E1AD3C84E00664C17 /* thread_checker.cpp */; };
|
67B52B601AD3C84E00664C17 /* thread_checker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67B52B5E1AD3C84E00664C17 /* thread_checker.cpp */; };
|
||||||
67B52B611AD3C84E00664C17 /* thread_checker.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67B52B5F1AD3C84E00664C17 /* thread_checker.hpp */; };
|
67B52B611AD3C84E00664C17 /* thread_checker.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67B52B5F1AD3C84E00664C17 /* thread_checker.hpp */; };
|
||||||
|
ACB634A3274D65F000F91940 /* math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ACB634A2274D65F000F91940 /* math.cpp */; };
|
||||||
E1B7FFC121FA19FE00F094DC /* thread_pool_computational.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBD21FA19FD00F094DC /* thread_pool_computational.hpp */; };
|
E1B7FFC121FA19FE00F094DC /* thread_pool_computational.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBD21FA19FD00F094DC /* thread_pool_computational.hpp */; };
|
||||||
E1B7FFC221FA19FE00F094DC /* thread_pool_delayed.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBE21FA19FD00F094DC /* thread_pool_delayed.hpp */; };
|
E1B7FFC221FA19FE00F094DC /* thread_pool_delayed.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBE21FA19FD00F094DC /* thread_pool_delayed.hpp */; };
|
||||||
E1B7FFC321FA19FE00F094DC /* thread_utils.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBF21FA19FD00F094DC /* thread_utils.hpp */; };
|
E1B7FFC321FA19FE00F094DC /* thread_utils.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E1B7FFBF21FA19FD00F094DC /* thread_utils.hpp */; };
|
||||||
|
@ -295,6 +296,7 @@
|
||||||
67B52B5F1AD3C84E00664C17 /* thread_checker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_checker.hpp; sourceTree = "<group>"; };
|
67B52B5F1AD3C84E00664C17 /* thread_checker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_checker.hpp; sourceTree = "<group>"; };
|
||||||
67C79B9E1E2929DB00C40034 /* checked_cast.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = checked_cast.hpp; sourceTree = "<group>"; };
|
67C79B9E1E2929DB00C40034 /* checked_cast.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = checked_cast.hpp; sourceTree = "<group>"; };
|
||||||
67E40EC71E4DC0D500A6D200 /* small_set_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = small_set_test.cpp; sourceTree = "<group>"; };
|
67E40EC71E4DC0D500A6D200 /* small_set_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = small_set_test.cpp; sourceTree = "<group>"; };
|
||||||
|
ACB634A2274D65F000F91940 /* math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = math.cpp; sourceTree = "<group>"; };
|
||||||
E1B7FFBD21FA19FD00F094DC /* thread_pool_computational.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_pool_computational.hpp; sourceTree = "<group>"; };
|
E1B7FFBD21FA19FD00F094DC /* thread_pool_computational.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_pool_computational.hpp; sourceTree = "<group>"; };
|
||||||
E1B7FFBE21FA19FD00F094DC /* thread_pool_delayed.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_pool_delayed.hpp; sourceTree = "<group>"; };
|
E1B7FFBE21FA19FD00F094DC /* thread_pool_delayed.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_pool_delayed.hpp; sourceTree = "<group>"; };
|
||||||
E1B7FFBF21FA19FD00F094DC /* thread_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_utils.hpp; sourceTree = "<group>"; };
|
E1B7FFBF21FA19FD00F094DC /* thread_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = thread_utils.hpp; sourceTree = "<group>"; };
|
||||||
|
@ -440,6 +442,7 @@
|
||||||
6753419C1A3F57E400A0A8C3 /* lower_case.cpp */,
|
6753419C1A3F57E400A0A8C3 /* lower_case.cpp */,
|
||||||
56290B89206D0887003892E0 /* lru_cache.hpp */,
|
56290B89206D0887003892E0 /* lru_cache.hpp */,
|
||||||
6753419D1A3F57E400A0A8C3 /* macros.hpp */,
|
6753419D1A3F57E400A0A8C3 /* macros.hpp */,
|
||||||
|
ACB634A2274D65F000F91940 /* math.cpp */,
|
||||||
6753419E1A3F57E400A0A8C3 /* math.hpp */,
|
6753419E1A3F57E400A0A8C3 /* math.hpp */,
|
||||||
6753419F1A3F57E400A0A8C3 /* matrix.hpp */,
|
6753419F1A3F57E400A0A8C3 /* matrix.hpp */,
|
||||||
672DD4B51E04255F0078E13C /* mem_trie.hpp */,
|
672DD4B51E04255F0078E13C /* mem_trie.hpp */,
|
||||||
|
@ -730,6 +733,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
ACB634A3274D65F000F91940 /* math.cpp in Sources */,
|
||||||
3446C6741DDCA96300146687 /* uni_string_dfa.cpp in Sources */,
|
3446C6741DDCA96300146687 /* uni_string_dfa.cpp in Sources */,
|
||||||
3446C6721DDCA96300146687 /* levenshtein_dfa.cpp in Sources */,
|
3446C6721DDCA96300146687 /* levenshtein_dfa.cpp in Sources */,
|
||||||
390F1C0E2294298E00EA58E3 /* file_name_utils.cpp in Sources */,
|
390F1C0E2294298E00EA58E3 /* file_name_utils.cpp in Sources */,
|
||||||
|
|
Loading…
Add table
Reference in a new issue