2010-10-16 4 views
6

Je voudrais avoir le nombre le plus proche en dessous de 1,0 en tant que virgule flottante. En lisant l'article de wikipedia sur IEEE-745 j'ai réussi à découvrir que la représentation binaire pour 1.0 est 3FF0000000000000, ainsi la double valeur la plus proche est réellement 0x3FEFFFFFFFFFFFFF.définit la constante double comme hexadécimale?

La seule façon que je connaisse pour initialiser un double avec ces données binaires est la suivante:

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

Ce qui est assez lourd à utiliser.

Existe-t-il une meilleure façon de définir ce double nombre, si possible en tant que constante?

+0

Puis-je vous demander pourquoi vous besoin? –

+0

la seule manière est ceci ... à moins que votre implémentation de C++ ait le support d'entier de 64 bits. –

+0

Ceci est juste nitpicking, mais il est IEEE-754-1985 (pas IEEE-745). – George

Répondre

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

Ce n'est pas sûr, mais quelque chose comme:

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

Cependant, cela repose sur un boutisme particulier de nombres à virgule flottante sur votre système, il ne faut pas faire cela!

Au lieu de cela, il suffit de mettre DBL_EPSILON dans <cfloat> (ou comme indiqué dans une autre réponse, std::numeric_limits<double>::epsilon()) à un bon usage.

+0

Le traiter comme un entier devrait le rendre indépendant de l'endian (sauf si vous avez un de ces étranges systèmes "mixtes") –

+0

@Rick Regan: Qui peut dire que la "endianness" de la représentation des types à virgule flottante est cohérente avec la représentation des types entiers? –

+0

Théoriquement, vous avez raison - mais avez-vous un exemple (en plus des "soft floats")? –

1

Si vous faites une bit_cast et utiliser fixed-width integer types, il peut être fait en toute sécurité:

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); 

Bien que vous pouvez probablement subtract epsilon from it.

+0

Je ne sais pas pourquoi vous vous êtes donné la peine de définir bit_cast alors que vous auriez pu utiliser 'reinterpret_cast ' directement. Cela semble toujours être une bonne solution. –

+0

@Mark: Cela ne serait pas statique affirmer que les deux types sont des types de POD, et il serait plus facile de briser les règles d'alias. (Certes, j'ai donné une solution plus générale que nécessaire, dans ce cas juste le faire directement fonctionne bien.) – GManNickG

0

C'est un peu archaïque, mais vous pouvez utiliser un union. En supposant un long long et un double sont tous deux 8 octets sur votre système:

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

Ici vous n'avez pas besoin de savoir à l'avance ce que la représentation binaire de 1,0 est.

+1

Cela conduit à UB, à proprement parler. – GManNickG

5

Le flottement hexadécimal et les doubles littéraux existent. La syntaxe est 0x1. (Mantisse) p (exposant en décimal) Dans votre cas, la syntaxe serait

double x = 0x1.fffffffffffffp-1 
+0

Je n'ai jamais entendu parler de cette syntaxe auparavant. Avez-vous une référence? –

+0

Je pense que c'est une partie de la norme C99. Cela fonctionne avec les compilateurs GNU, je ne parle pas des autres. – Shum

+0

@Mark Ransom: J'ai écrit un article à ce sujet récemment: http://www.exploringbinary.com/hexadecimal-floating-point-constants/ –

Questions connexes