2009-03-03 8 views
32

mi piacerebbe una funzione floor con la sintassiIl casting in un file int dopo std :: floor garantisce il risultato corretto?

int floor(double x); 

ma std::floor restituisce un double. È

static_cast <int> (std::floor(x)); 

garantito per darmi il numero intero corretto, o potrei avere un problema di off-by-one? Sembra funzionare, ma mi piacerebbe sapere per certo.

Per i punti bonus, perché diavolo std::floor restituisce un double in primo luogo?

risposta

25

L'intervallo di double è molto superiore all'intervallo di numeri interi a 32 o 64 bit, motivo per cui std::floor restituisce double. La trasmissione a int dovrebbe andare bene fintanto che rientri nell'intervallo appropriato, ma sappi che uno double non può rappresentare esattamente tutti gli interi a 64 bit, quindi potresti anche finire con errori quando vai oltre il punto in cui la precisione di double è tale che la differenza tra due doppie consecutive è maggiore di 1.

+0

quindi, se è nella gamma di destra, il cast sta bene dove sta scritto (cosa che implica) questo nelle specifiche? ? –

+0

Bene, ci sono due operazioni qui: std :: floor, e il cast. Std :: floor è pensato per restituire un intero, e il cast è specificato in termini di un valore intero. Finché std :: floor restituisce un valore che è veramente un numero esatto, io ca non vedere come avrebbe senso che fallisse. –

+0

In che modo floor restituirà mai qualcosa che non è un intero esatto? Per i piccoli doppi (epsilon << 1), è possibile rappresentare tutti i valori interi, incluso floor (x). Per big doubles (epsilon >> 1), solo i valori interi possono essere rappresentati, quindi floor (x) == x. – MSalters

12
static_cast <int> (std::floor(x)); 

fa praticamente ciò che si vuole, sì. Ti dà il numero intero più vicino, arrotondato verso -infinità. Almeno fino a quando il tuo input si trova nell'intervallo rappresentabile da ints. Non sono sicuro di cosa intendi con 'l'aggiunta di .5 e quant'altro, ma non avrà lo stesso effetto

E std :: floor restituisce un doppio perché è il più generale. A volte potresti voler arrotondare un float o raddoppiare, ma preservare il tipo. Cioè, da 1,3 a 1,0f, anziché a 1.

Sarebbe difficile da fare se std :: floor restituisse un int. (o almeno avresti un cast addizionale in più che rallenta le cose).

Se floor esegue solo l'arrotondamento stesso, senza modificare il tipo, è possibile eseguire il cast su int se/quando è necessario.

Un altro motivo è che la gamma dei doppi è molto maggiore di quella degli interi. Potrebbe non essere possibile arrotondare tutti i doppi a int.

+1

floor() arrotonda verso -infinity, non zero: vedi http://www.cplusplus.com/reference/clibrary/cmath/floor.html –

+0

doh, hai ragione, naturalmente. Fisso. – jalf

5

Se si desidera gestire varie condizioni numeriche e si desidera gestire diversi tipi di conversioni in modo controllato, è possibile che si debba guardare allo Boost.NumericConversion. Questa libreria permette di gestire casi strani (come out-of-range, l'arrotondamento, gamme, ecc)

Ecco l'esempio dalla documentazione:

#include <cassert> 
#include <boost/numeric/conversion/converter.hpp> 

int main() { 

    typedef boost::numeric::converter<int,double> Double2Int ; 

    int x = Double2Int::convert(2.0); 
    assert (x == 2); 

    int y = Double2Int()(3.14); // As a function object. 
    assert (y == 3) ; // The default rounding is trunc. 

    try 
    { 
     double m = boost::numeric::bounds<double>::highest(); 
     int z = Double2Int::convert(m); // By default throws positive_overflow() 
    } 
    catch (boost::numeric::positive_overflow const&) 
    { 
    } 

    return 0; 
} 
2

La maggior parte della libreria matematica standard utilizza doppie ma fornisce anche versioni flottanti. std :: floorf() è la versione di precisione singola di std :: floor() se preferisci non usare i doppi.

Modifica: ho rimosso parte della mia risposta precedente. Avevo dichiarato che il pavimento era ridondante quando si lanciava su int, ma ho dimenticato che questo è vero solo per i numeri in virgola mobile positivi.

6

Il C++ norma dice (4.9.1):

"An rvalue di un tipo a virgola mobile può essere convertito in un rvalue di un tipo intero La conversione tronca, cioè la parte frazionaria viene scartata..Il comportamento non è definito se il valore troncato non può essere rappresentato nel tipo di destinazione ".

Quindi se si converte un double in un int, il numero è compreso nell'intervallo di int e l'arrotondamento richiesto è verso zero, allora è sufficiente per lanciare semplicemente il numero di int:

(int) x;

+0

Il troncamento della parte frazione non è piano - il risultato è diverso per i numeri negativi. – Suma

+0

Ho scritto chiaramente "Quindi se ... l'arrotondamento richiesto è verso zero" per distinguere questo caso da "arrotondamento verso l'infinito". Il piano si sta arrotondando verso l'infinito, il troncamento verso lo zero. –