본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::numeric_limits (98, 11, 17)

by snowoods 2025. 9. 7.

Modern C++

수치 한계와 특성 (Numeric Limits with std::numeric_limits)

 

개요

std::numeric_limits는 C++ 표준 라이브러리의 <limits> 헤더에 정의된 템플릿 클래스로, 기본 숫자 타입들의 속성과 한계를 조회하는 데 사용됩니다. 이 클래스를 통해 타입의 최대값, 최소값, 정밀도 등 다양한 수치적 특성을 컴파일 타임에 얻을 수 있습니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98: 기본 std::numeric_limits 템플릿 클래스 도입
  • C++11: lowest(), max_digits10 등 추가 멤버 함수 도입
  • C++17: is_iec559를 통한 IEEE 754 부동소수점 준수 여부 확인

 

내용 설명

1. 기본 사용법

std::numeric_limits는 템플릿 클래스로, 특정 타입에 대한 수치적 특성을 조회할 수 있습니다. 정수형과 부동소수점 타입 모두에 대해 특화되어 있습니다.

2. 주요 멤버 상수 및 함수

  • min(): 타입이 표현할 수 있는 최소 유한값
  • max(): 타입이 표현할 수 있는 최대 유한값
  • lowest(): 타입이 표현할 수 있는 가장 작은 값 (부동소수점의 경우 -max()와 동일)
  • epsilon(): 1과 1+epsilon이 구분될 수 있는 가장 작은 값 (부동소수점 전용)
  • digits: 가수부 비트 수 (부동소수점) 또는 비트 폭 (정수형)
  • digits10: 10진수로 표현 가능한 자릿수
  • is_signed: 부호 있는 타입인지 여부
  • is_integer: 정수형인지 여부
  • is_exact: 정확한 표현이 가능한지 여부
  • has_infinity: 무한대를 표현할 수 있는지 여부

3. 부동소수점 비교

부동소수점 숫자는 이진 표현의 한계로 인해 정확한 비교가 어려울 수 있습니다. epsilon()을 사용하여 두 부동소수점이 근사적으로 같은지 비교할 수 있습니다.

 

예제 코드

#include <cmath>
#include <cstdint>
#include <iostream>
#include <limits>

template <typename T>
void print_type_properties()
{
    std::cout << "min=" << std::numeric_limits<T>::min() << '\n'
              << "max=" << std::numeric_limits<T>::max() << '\n'
              << "bits=" << std::numeric_limits<T>::digits << '\n'
              << "decdigits=" << std::numeric_limits<T>::digits10 << '\n'
              << "integral=" << std::boolalpha
              << std::numeric_limits<T>::is_integer << '\n'
              << "signed=" << std::boolalpha
              << std::numeric_limits<T>::is_signed << '\n'
              << "exact=" << std::boolalpha << std::numeric_limits<T>::is_exact
              << '\n'
              << "infinity=" << std::boolalpha
              << std::numeric_limits<T>::has_infinity << '\n'
              << '\n';
}

template <typename T>
bool equal(const T x, const T y)
{
    return x == y;
}

template <typename T>
bool almost_equal(const T x, const T y)
{
    return std::abs(x - y) <= std::numeric_limits<T>::epsilon();
}

int main()
{
    std::cout << "=== std::uint16_t ===\n";
    print_type_properties<std::uint16_t>();

    std::cout << "=== std::int32_t ===\n";
    print_type_properties<std::int32_t>();

    std::cout << "=== float ===\n";
    print_type_properties<float>();

    const auto d1 = 0.2;
    const auto d2 = 1.0 / (std::sqrt(5.0) * std::sqrt(5.0)); // == 1 / 5

    std::cout << "d1 = " << d1 << '\n';
    std::cout << "d2 = " << d2 << '\n';

    std::cout.precision(17);

    std::cout << "d1 (high precision) = " << d1 << '\n';
    std::cout << "d2 (high precision) = " << d2 << '\n';

    std::cout << "d1 == d2? " << std::boolalpha << equal(d1, d2) << '\n';

    std::cout << "epsilon = " << std::numeric_limits<float>::epsilon() << '\n';
    std::cout << "d1 ~= d2? " << std::boolalpha << almost_equal(d1, d2) << '\n';

    return 0;
}

 

실행 결과

=== std::uint16_t ===
min=0
max=65535
bits=16
decdigits=4
integral=true
signed=false
exact=true
infinity=false

=== std::int32_t ===
min=-2147483648
max=2147483647
bits=31
decdigits=9
integral=true
signed=true
exact=true
infinity=false

=== float ===
min=1.17549e-38
max=3.40282e+38
bits=24
decdigits=6
integral=false
signed=true
exact=false
infinity=true

d1 = 0.2
d2 = 0.2
d1 (high precision) = 0.20000000000000001
d2 (high precision) = 0.20000000000000004
d1 == d2? false
epsilon = 1.19209e-07
d1 ~= d2? true

 

활용팁

  1. 타입 독립적인 코드 작성 시: std::numeric_limits를 사용하면 템플릿 코드에서 타입에 따라 다른 동작을 구현할 수 있습니다.
  2. 안전한 수치 연산: 오버플로우나 언더플로우를 방지하기 위해 연산 전에 min()max()를 확인하세요.
  3. 부동소수점 비교: == 연산자 대신 std::abs(a - b) < epsilon 패턴을 사용하여 부동소수점을 비교하세요.
  4. 플랫폼 독립성: intlong 같은 타입의 크기는 플랫폼에 따라 다를 수 있으므로, 특정 크기가 필요한 경우 int32_t 같은 고정 크기 정수형을 사용하세요.
  5. 컴파일 타임 상수: std::numeric_limits의 멤버들은 컴파일 타임 상수이므로, static_assert와 함께 사용하여 타입 요구사항을 검증할 수 있습니다.