2010-04-06 11 views
6

Il doppio ha un intervallo superiore a un numero intero a 64 bit, ma la sua precisione è inferiore alla sua rappresentazione (poiché il doppio è anche 64 bit, può montare più valori effettivi). Quindi, quando si rappresentano interi più grandi, si inizia a perdere la precisione nella parte intera.Trova la dimensione massima integer che un tipo a virgola mobile può gestire senza perdita di precisione

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

Questo stampa:

2147483647 
2147483647.000000 
9223372036854775807 
9223372036854775800.000000 
2147483647 
2147483648.000000 

notare come max int può inserirsi in un double senza perdita di precisione e boost::intmax_t (64-bit in questo caso) non può. float non può nemmeno contenere un int.

Ora, la domanda: esiste un modo in C++ per verificare se l'intero intervallo di un determinato tipo intero può adattarsi a un tipo di punto di loating senza perdita di precisione?

Preferibilmente,

  • sarebbe un controllo della fase di compilazione che può essere utilizzato in un asserzione statica,
  • e non comporterebbe enumerare le costanti compilatore deve sapere o può calcolare.
+1

Perché devi controllare? La parte intera ha 52 bit di precisione, quindi è quanto ottieni. –

+0

Una volta determinato il limite, non puoi semplicemente definire un CONST? –

+2

@Billy: _Technically_ C++ non richiede IEEE 754 in virgola mobile, quindi supponendo che l'implementazione utilizzi IEEE 754 non è portatile (il fatto che IEEE 754 sia onnipresente nonostante). –

risposta

6

Solo un po predicato:

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

Verifica se la precisione binario disponibile in una T esiste in un U. Funziona su tutti i tipi.


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

5

È possibile utilizzare std::numeric_limits<T>::digits per sapere quanta precisione binaria si dispone. es:

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

Sto usando VC, questo è il motivo per cui si vede 'long long' nella mia risposta anche se non è ancora standard. – AraK

+1

'long long' è accettato anche da GCC e ICC. Sono sicuro che anche DMC e alcuni altri lo fanno. È un'estensione * molto * comune. – greyfade

+0

Almeno è meglio che usare, ad esempio __int64. –