2010-10-16 6 views
6

Mi piacerebbe avere il numero più vicino sotto 1.0 come un punto in virgola mobile. Leggendo l'articolo di wikipedia su IEEE-745 sono riuscito a scoprire che la rappresentazione binaria per 1.0 è 3FF0000000000000, quindi il doppio valore più vicino è in realtà 0x3FEFFFFFFFFFFFFF.definisce la doppia costante come esadecimale?

L'unico modo che conosco per inizializzare una doppia con questi dati binari è questo:

double a; 
*((unsigned*)(&a) + 1) = 0x3FEFFFFF; 
*((unsigned*)(&a) + 0) = 0xFFFFFFFF; 

che è piuttosto ingombrante da usare.

C'è un modo migliore per definire questo doppio numero, se possibile come costante?

+0

Posso chiedere perché avete bisogno di questo? –

+0

l'unico modo è questo ... a meno che l'implementazione del C++ non abbia il supporto intero a 64 bit. –

+0

Questo è solo nitpicking, ma è IEEE-754-1985 (non IEEE-745). – George

risposta

2
#include <iostream> 
#include <iomanip> 
#include <limits> 
using namespace std; 

int main() 
{ 
    double const x = 1.0 - numeric_limits<double>::epsilon(); 

    cout 
     << setprecision(numeric_limits<double>::digits10 + 1) << fixed << x 
     << endl; 
} 
3

Non è sicuro, ma qualcosa di simile:

double a; 
*(reinterpret_cast<uint64_t *>(&a)) = 0x3FEFFFFFFFFFFFFFL; 

Tuttavia, questo si basa su un particolare endianness di numeri in virgola mobile sul vostro sistema, in modo da non farlo!

Invece, è sufficiente inserire DBL_EPSILON in <cfloat> (o come indicato in un'altra risposta, std::numeric_limits<double>::epsilon()) per un buon utilizzo.

+0

Trattarlo come un intero dovrebbe renderlo indipendente dall'endian (a meno che tu non abbia uno di quei bizzarri sistemi "mixed-endian") –

+0

@Rick Regan: chi dice che la "endianità" della rappresentazione della piattaforma dei tipi a virgola mobile è coerente con la rappresentazione di tipi interi? –

+0

In teoria hai ragione, ma hai un esempio (oltre ai "soft float" mixed-endian)? –

1

Se si commette un bit_cast e utilizzare fixed-width integer types, può essere fatto in modo sicuro:

template <typename R, typename T> 
R bit_cast(const T& pValue) 
{ 
    // static assert R and T are POD types 

    // reinterpret_cast is implementation defined, 
    // but likely does what you expect 
    return reinterpret_cast<const R&>(pValue); 
} 

const uint64_t target = 0x3FEFFFFFFFFFFFFFL; 
double result = bit_cast<double>(target); 

anche se si può probabilmente solo subtract epsilon from it.

+0

Non sei sicuro del motivo per cui hai deciso di definire bit_cast quando avresti potuto usare 'reinterpret_cast ' direttamente. Sembra ancora una buona soluzione. –

+0

@Mark: non sarebbe statico asserire che entrambi i tipi sono tipi POD e renderebbe più semplice interrompere le regole di aliasing. (Certo, ho dato una soluzione più generale di quanto richiesto, in questo caso solo farlo direttamente funziona bene.) – GManNickG

0

È un po 'arcaico, ma è possibile utilizzare uno union. Assumendo una long long e double sono entrambi 8 byte lungo sul vostro sistema:

typedef union { long long a; double b } my_union; 

int main() 
{ 
    my_union c; 
    c.b = 1.0; 
    c.a--; 
    std::cout << "Double value is " << c.b << std::endl; 
    std::cout << "Long long value is " << c.a << std::endl; 
} 

Qui non è necessario conoscere in anticipo ciò che la rappresentazione po 'di 1.0 è.

+1

Che porta a UB, in senso stretto. – GManNickG

5

Esistono galleggianti esadecimali e doppi letterali. La sintassi è 0x1. (Mantissa) p (esponente in decimale) Nel tuo caso la sintassi sarebbe

double x = 0x1.fffffffffffffp-1 
+0

Non ho mai sentito parlare di questa sintassi prima. Hai un riferimento? –

+0

Penso che sia parte dello standard C99. Funziona con i compilatori GNU, non sugli altri. – Shum

+0

@Mark Ransom: Ho scritto un articolo su questo recentemente: http://www.exploringbinary.com/hexadecimal-floating-point-constants/ –