2010-04-06 4 views
6

Podwójny ma zakres większy niż 64-bitowa liczba całkowita, ale jego dokładność jest mniejsza niż składki do jej reprezentacji (ponieważ podwójne to również 64-bitowe, może dopasowują więcej rzeczywistych wartości). Tak więc, gdy reprezentujesz większe liczby całkowite, zaczynasz tracić precyzję w części całkowitej.Znajdź maksymalną liczbę całkowitą, którą może obsłużyć typ zmiennoprzecinkowy bez utraty precyzji.

#include <boost/cstdint.hpp> 
#include <limits> 

template<typename T, typename TFloat> 
void 
maxint_to_double() 
{ 
    T i = std::numeric_limits<T>::max(); 
    TFloat d = i; 
    std::cout 
     << std::fixed 
     << i << std::endl 
     << d << std::endl; 
} 

int 
main() 
{ 
    maxint_to_double<int, double>(); 
    maxint_to_double<boost::intmax_t, double>(); 
    maxint_to_double<int, float>(); 
    return 0; 
} 

Drukuje:

2147483647 
2147483647.000000 
9223372036854775807 
9223372036854775800.000000 
2147483647 
2147483648.000000 

Zauważ max int może pasować do double bez utraty precyzji i boost::intmax_t (64-bit w tym przypadku) nie może. float nie może nawet posiadać numeru int.

Teraz pytanie: jest sposób w C++, aby sprawdzić, czy cały zakres danego typu integer może pasować do typu punktu loating bez utraty precyzji?

Korzystnie

  • byłoby sprawdzenie kompilacji, które mogą być używane w statycznym twierdzenia
  • i nie pociąga za sobą wyliczanie stałych kompilator powinien wiedzieć czy można obliczyć.
+1

Dlaczego trzeba sprawdzić? Część całkowita ma 52 bity precyzji, więc to, ile dostajesz. –

+0

Po ustaleniu limitu nie możesz zdefiniować CONST? –

+2

@Billy: _Technically_ C++ nie wymaga zmiennoprzecinkowego IEEE 754, więc zakładając, że implementacja wykorzystuje IEEE 754, nie jest przenośna (fakt, że IEEE 754 jest wszechobecny). –

Odpowiedz

6

Tylko trochę orzecznik:

#include <limits> 

template <typename T, typename U> 
struct can_fit 
{ 
    static const bool value = std::numeric_limits<T>::digits 
          <= std::numeric_limits<U>::digits; 
}; 

#include <iostream> 

int main(void) 
{ 
    std::cout << std::boolalpha; 

    std::cout << can_fit<short, float>::value << std::endl; 
    std::cout << can_fit<int, float>::value << std::endl; 

    std::cout << can_fit<int, double>::value << std::endl; 
    std::cout << can_fit<long long, double>::value << std::endl; 

    std::cout << can_fit<short, int>::value << std::endl; 
    std::cout << can_fit<int, short>::value << std::endl; 
} 

Testy jeśli binarny precyzja dostępny w T istnieje w U. Działa na wszystkich typach.


"Boostified":

// this is just stuff I use 
#include <boost/type_traits/integral_constant.hpp> 

template <bool B> 
struct bool_type : boost::integral_constant<bool, B> 
{ 
    static const bool value = B; 
}; 

typedef const boost::true_type& true_tag; 
typedef const boost::false_type& false_tag; 

// can_fit type traits 
#include <limits> 

namespace detail 
{ 
    template <typename T, typename U> 
    struct can_fit 
    { 
     static const bool value = std::numeric_limits<T>::digits 
           <= std::numeric_limits<U>::digits; 
    }; 
} 

template <typename T, typename U> 
struct can_fit : bool_type<detail::can_fit<T, U>::value> 
{ 
    typedef T type1; 
    typedef U type2; 

    static const bool value = detail::can_fit<T, U>::value; 
}; 

// test 
#include <iostream> 

namespace detail 
{ 
    void foo(true_tag) 
    { 
     std::cout << "T fits in U" << std::endl; 
    } 

    void foo(false_tag) 
    { 
     std::cout << "T does not fit in U" << std::endl; 
    } 
} 

// just an example 
template <typename T, typename U> 
void foo(void) 
{ 
    detail::foo(can_fit<T, U>()); 
} 

int main(void) 
{ 
    foo<int, double>(); 
} 
+0

+1 dla "can_fit'. – AraK

5

Możesz użyć numeru std::numeric_limits<T>::digits, aby dowiedzieć się, ile masz dokładności binarnej. np:

int binary_digits_double = numeric_limits<double>::digits;  // 53 
int binary_digits_long_long = numeric_limits<long long>::digits; // 63 
int binary_digits_uint = numeric_limits<unsigned int>::digits; // 32 
+0

Używam VC, to dlatego widzisz 'long long' w mojej odpowiedzi, chociaż nie jest jeszcze standardem. – AraK

+1

'long long' jest również akceptowane przez GCC i ICC. Jestem pewien, że DMC i kilka innych też. Jest to * bardzo * powszechne rozszerzenie. – greyfade

+0

Przynajmniej jest lepsze niż użycie, powiedz __int64. –

Powiązane problemy