엔지니어 동행하기

std::numeric_limits<T>::epsilon(), 0에 가까운 값, Floating Point비교 본문

Modern C++

std::numeric_limits<T>::epsilon(), 0에 가까운 값, Floating Point비교

엔지니어 설리번 2022. 7. 10. 11:24
반응형
Floating Point 가 같은지 비교할 때, == operator를 사용하면 의도하지 않은 결과를 얻게 됩니다. 그 이유는 컴퓨터가 Floating Point를 정확히 그 값으로 가지고 있는 것이 아니라 그 값과 가장 가까운, 컴퓨터로 표현할 수 있는 값으로  가지고 있기 때문입니다.  그렇다면 Floating Point가 같은지 비교하기 위해서는 어떻게 해야 하는지에 대한 내용을 정리하도록 하겠습니다. 

 

epsilon()

epsilon() 함수는 1.0과 Type T로 나타낼 수 있는 1.0보다 큰 가장 작은 값의 차이를 출력합니다. 즉, Type T로 나타낼 수 있는 0에 가장 가까운 값을 반환합니다.

 

예제 1 : Floating Point가 같은 값인지 판단 

epsilon()함수의 반환 값을 이용하면 Floating Point가 같은지 비교할 수 있습니다. 아래 코드의 almost_equal 함수를 그대로 이용하면 되고, Type T에는 float, double, long double 같은 type이 올 수 있습니다. 

#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <algorithm>
 
template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
{
    // the machine epsilon has to be scaled to the magnitude of the values used
    // and multiplied by the desired precision in ULPs (units in the last place)
    return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
        // unless the result is subnormal
        || std::fabs(x-y) < std::numeric_limits<T>::min();
}
 
int main()
{
    double d1 = 0.2;
    double d2 = 1 / std::sqrt(5) / std::sqrt(5);
    std::cout << std::fixed << std::setprecision(20) 
        << "d1=" << d1 << "\nd2=" << d2 << '\n';
 
    if(d1 == d2)
        std::cout << "d1 == d2\n";
    else
        std::cout << "d1 != d2\n";
 
    if(almost_equal(d1, d2, 2))
        std::cout << "d1 almost equals d2\n";
    else
        std::cout << "d1 does not almost equal d2\n";
}

Output: 

d1=0.20000000000000001110
d2=0.19999999999999998335
d1 != d2
d1 almost equals d2

Output을 보면, d1에 0.2를 저장해도 출력해보면 뒤쪽에 값이 더 존재하고, d2에 수식적으로 1/5를 저장해도 출력해보면 정확히 0.2와 같지 않은 것을 알 수 있습니다. 따라서 Floating Point가 같은지 비교할 때는 두 값을 빼고, eplsion() 함수를 활용해 그 차이가 0에 매우 가까운지로 판단합니다. 

 

예제 2 : Floating Point 값이 0인지 판단

같은 맥락에서 Floating Point의 값이 0인지 판단할 때, == operator를 사용할 수 없습니다. ApolloAuto 코드에서 epsilon()함수를 활용하여 물체의 속도가 0인지 판단하고 있습니다.

// Calculate Object's velocity

constexpr double kEpsilon = std::numeric_limits<double>::epsilon();
if (velocity < kEpsilon) {  
  return true; // 속도를 0으로 판단
}

https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon

 

std::numeric_limits<T>::epsilon - cppreference.com

static T epsilon() throw(); (until C++11) static constexpr T epsilon() noexcept; (since C++11) Returns the machine epsilon, that is, the difference between 1.0 and the next value representable by the floating-point type T. It is only meaningful if std::num

en.cppreference.com

https://github.com/ApolloAuto/apollo/blob/master/modules/perception/lidar/lib/tracker/multi_lidar_fusion/mlf_motion_refiner.cc

 

GitHub - ApolloAuto/apollo: An open autonomous driving platform

An open autonomous driving platform. Contribute to ApolloAuto/apollo development by creating an account on GitHub.

github.com

(예제 : CheckStaticHypothesisByVelocityAngleChange 함수에서 velocity가 0인지 판단할 때 사용)

반응형
Comments