2010-04-06 20 views
6

El doble tiene un rango mayor que un entero de 64 bits, pero su precisión es menos costosa para su representación (ya que el doble también es de 64 bits; se ajusta a más valores reales). Entonces, al representar números enteros más grandes, comienzas a perder precisión en la parte entera.Buscar el tamaño máximo entero que un tipo de punto flotante puede manejar sin pérdida de precisión

#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; 
} 

Esta impresora:

2147483647 
2147483647.000000 
9223372036854775807 
9223372036854775800.000000 
2147483647 
2147483648.000000 

Nota cómo máximo int puede caber en un double sin pérdida de precisión y boost::intmax_t (64 bits en este caso) no puede. float ni siquiera puede contener un int.

Ahora, la pregunta: ¿Hay alguna manera en C++ para verificar si todo el rango de un tipo de entero dado puede caber en un tipo de punto de equilibrio sin pérdida de precisión?

Preferiblemente,

  • sería una comprobación en tiempo de compilación que se puede utilizar en una afirmación estática,
  • y no implicaría la enumeración de las constantes del compilador debe saber o puede calcular.
+1

¿Por qué tienes que comprobar? La parte entera tiene 52 bits de precisión, así que eso es cuánto obtienes. –

+0

Una vez que haya determinado el límite, ¿no puede simplemente definir un CONST? –

+2

@Billy: _Technically_ C++ no requiere punto flotante IEEE 754, por lo que suponiendo que la implementación usa IEEE 754 no es portátil (a pesar de que el IEEE 754 es omnipresente). –

Respuesta

6

Sólo un poco predicado:

#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; 
} 

Comprueba si existe la precisión binaria disponible en una T en un U. Funciona en todos los tipos.


"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 para 'can_fit'. – AraK

5

Puede usar std::numeric_limits<T>::digits para saber cuánta precisión binaria tiene. por ejemplo:

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

Estoy usando VC, por eso ves 'long long' en mi respuesta, aunque todavía no es estándar. – AraK

+1

'long long' también es aceptado por GCC e ICC. Estoy seguro de que DMC y algunos otros también lo hacen. Es una extensión * muy * común. – greyfade

+0

Al menos es mejor que usar, digamos __int64. –

Cuestiones relacionadas