728x90
반응형

C++ 테스트 프레임워크에서의 부동소수점(float, double) 근사 비교(Approximate Comparison)


1. C++ 부동소수점 비교 기능 프레임워크

  • 실수(부동소수점) 계산은 계산 과정에서 아주 작은 오차가 누적되기 때문에, == 같은 정확한 비교는 거의 항상 실패합니다.
  • 따라서 C++ 테스트 코드에서는 일정 오차 허용 범위 내의 비교, 즉 근사 비교(Approximate Comparison) 가 필수입니다.


  • 각 프레임워크는 다른 방식으로 오차를 정의하고 비교하므로, 정확한 테스트를 위해 각 기능의 특징을 이해하고 사용하는 것이 중요합니다.


2. 프레임워크별 부동소수점 근사 비교 기능

2.1 doctest: doctest::Approx()

  • cpp

      #include <doctest/doctest.h>
      
      TEST_CASE("예제") {
          double halfpi = 1.5707963;
          REQUIRE(halfpi == doctest::Approx(1.5707963));
      }
    

  • epsilon() 으로 상대 오차 정밀도 조절 가능
  • 기본 epsilon = 1e-12
  • 오차 계산:
    • cpp

        std::fabs(lhs - rhs) <= epsilon × max(|lhs|, |rhs|)
      

2.1.1. 예시 비교 결과

  • 비교값 통과 여부 (기본 설정 기준)
    1.57079629 ✅ 통과
    1.57079631 ✅ 통과
    1.5707964 ❌ 실패 가능성 높음

2.1.2. 정밀도 조절 예

  • cpp

      REQUIRE(1.57079631 == doctest::Approx(1.5707963).epsilon(1e-9));  // 허용
      REQUIRE(1.57079631 == doctest::Approx(1.5707963).epsilon(1e-13)); // 실패 가능
    


2.2 Catch2: Catch::Approx()

  • cpp

      #include <catch2/catch.hpp>
    
      TEST_CASE("Catch2 근사 비교") {
          double val = 1.5707963;
          REQUIRE(val == Catch::Approx(1.5707963));
      }
    

  • 사용법 및 APIdoctest와 거의 동일
  • .epsilon(), .margin()으로 정밀도 조절 가능


2.3. Google Test: EXPECT_NEAR()

  • cpp

      #include <gtest/gtest.h>
    
      TEST(FloatingPointTest, NearTest) {
          double val = 1.5707963;
          EXPECT_NEAR(val, 1.5707963, 1e-9);  // 절대 오차
      }
    

  • 세 번째 인자로 절대 오차를 지정 (1e-9)
  • 상대 오차는 직접 계산해야 함


2.4. Boost.Test: BOOST_CHECK_CLOSE()

  • cpp

      #include <boost/test/unit_test.hpp>
      
      BOOST_AUTO_TEST_CASE(test_close) {
          double val = 1.5707963;
          BOOST_CHECK_CLOSE(val, 1.5707963, 0.0001);  // 0.0001% 허용 오차
      }
    

  • 상대 오차를 퍼센트(%) 로 지정
  • 오차 계산: 100 * |a - b| / max(|a|, |b|)


2.5 Eigen: isApprox()

  • cpp

      #include <Eigen/Dense>
      
      Eigen::VectorXd a(2), b(2);
      a << 1.0, 2.0;
      b << 1.0, 2.000000001;
      
      bool is_close = a.isApprox(b, 1e-9);
    

  • 주로 벡터/행렬 비교에 사용
  • 상대 오차 비교 (기본값 존재)


2.6. C++ 표준 방식 (직접 구현)

  • cpp

      #include <cmath>
      
      double a = 1.5707963;
      double b = 1.57079631;
      double epsilon = 1e-9;
      
      if (std::fabs(a - b) < epsilon) {
          // 근사적으로 같다
      }
    

  • 가장 쉽게 구현하는 방식
  • 테스트 프레임워크 메시지가 없어 불편함


3. 요약

  • 프레임워크 함수 오차 방식 조절 방식 특징
    doctest doctest::Approx() 상대 오차 .epsilon(), .scale() 직관적, Catch2와 유사
    Catch2 Catch::Approx() 상대/절대 혼합 .epsilon(), .margin() 직관적, doctest와 유사
    Google Test EXPECT_NEAR() 절대 오차 세 번째 인자로 오차 간단하지만 유연성 낮음
    Boost.Test BOOST_CHECK_CLOSE() 상대 오차 (%) 퍼센트 지정 오래된 스타일의 매크로
    Eigen isApprox() 상대/절대 오차 두 번째 인자로 오차 벡터/행렬에 적합
    C++ 표준 방식 std::fabs(a - b) < e 절대 오차 직접 구현 메시지 부족, 보조적 사용


4. 정리

  • doctest::Approx()는 작고 빠르면서도 강력한 부동소수점 근사 비교 기능을 제공합니다.
  • Catch2::Approx()와 거의 동일한 인터페이스를 가지며, 상대 오차 기반으로 정밀도 제어가 가능합니다.

  • 보다 복잡한 비교나 절대 오차 비교가 필요한 경우 Google Test, Boost.Test, Eigen 등 다른 프레임워크의 기능을 활용하면 됩니다.
  • 어떤 방식이든, 부동소수점은 == 비교가 아니라 근사 비교가 기본 이 되어야 합니다.
  • 테스트 정확도를 높이고자 한다면, 오차 허용 기준을 명확하게 정의 하고 그에 맞는 도구를 선택하는 것이 중요합니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형

+ Recent posts