34 votes

Est-il garanti de conserver un flotteur lorsqu’il est transporté par un double en C / C ++?

En supposant que la norme IEEE-754 la conformité, est un flotteur garanti d'être préservés lorsqu'ils sont transportés à travers un double?

En d'autres termes, les éléments suivants affirmer toujours être satisfait?

int main()
{
    float f = some_random_float();
    assert(f == (float)(double)f);
}

Supposons que f pourrait acquérir l'une quelconque des valeurs définies par la norme IEEE, comme le NaN et de l'Infini.

Selon IEEE, est-il un cas où l'assertion sera satisfait, mais l'exacte au niveau des bits de la représentation n'est pas conservé après le transport à travers la double?

L'extrait de code est valable en C et C++.

30voto

Steve Jessop Points 166970

Vous n'avez même pas besoin de supposer l'IEEE. C89 dit dans 3.1.2.5:

L'ensemble des valeurs du type float est un sous-ensemble de l'ensemble de valeurs le type double

Et tous les autres de C et de C++ standard, dit l'équivalent de choses. Autant que je sache, NaNs et les infinis sont "les valeurs de type float", bien que les valeurs de certains cas à des règles lorsqu'il est utilisé comme opérandes.

Le fait que le float -> double -> float conversion restaure la valeur d'origine de l' float (en général) dans le fait que les conversions numériques, tous de préserver la valeur si elle est représentable dans le type de destination.

Au niveau des bits les représentations sont un peu différente de la matière. Imaginez qu'il y ait une valeur de float qui a deux distincts au niveau du bit représentations. Ensuite, rien dans la norme C empêche le float -> double -> float conversion de changer de l'un à l'autre. Dans la norme IEEE cela n'arrivera pas pour les "valeurs réelles", sauf si il y a un rembourrage bits, mais je ne sais pas si IEEE règles d'une seule NaN ayant distinctes au niveau du bit représentations. NaNs ne comparez pas égal à eux-mêmes de toute façon, donc il n'y a également aucun moyen standard pour dire si deux NaNs sont "les mêmes NaN" ou "différents NaNs", sauf peut-être les convertir en chaînes. Le problème est peut-être discutable.

Une chose à regarder dehors pour est non conforme modes de compilateurs, dans laquelle elle tient super-valeurs précises "sous les couvertures", par exemple les résultats intermédiaires de gauche dans les registres à virgule flottante et réutilisés sans arrondi. Je ne pense pas que serait la cause de votre code d'exemple à l'échec, mais dès que vous êtes en train de faire à virgule flottante == c'est le genre de chose que vous commencent à s'inquiéter.

16voto

Alexey Frunze Points 37651

De C99:

6.3.1.5 Réel les types flottants
1 Lorsqu'un flotteur est promu à double ou long double, ou un double en est promu au long double, sa valeur est inchangée.
2 Lorsqu'un double est rétrogradé à flotteur, un long double est rétrogradé à double ou float, ou une valeur représentée dans plus de précision et de portée que requis par son type sémantique (voir 6.3.1.8) est explicitement converti en son genre sémantique, si la valeur convertie peut être représenté exactement dans le nouveau type, il est inchangé...

Je pense que, cela vous garantit qu'un float->double->float conversion va conserver l'original en valeur flottante.

La norme définit également les macros INFINITY et NAN en 7.12 Mathematics <math.h>:

4 La macro INFINI s'étend à une expression constante de type float représentant de positif ou de l'infini, si disponibles; d'autre à une constante positive de type float qui déborde à la durée de traduction.
5 La macro NAN est définie si et seulement si l'application prend en charge calme NaNs pour le type float. Il se développe à une expression constante de type float représentant un calme NaN.

Donc, il y a disposition pour de telles valeurs spéciales et les conversions peuvent travailler pour eux (y compris pour le moins l'infini et le zéro négatif).

2voto

Lasse Reinhold Points 21

L'assertion échoue dans les chasses à zéro et/ou dénormalisée-est-le mode de zéro (par exemple, le code compilé avec -mfpmath=sse, -rapide-mathématiques, etc, mais aussi sur des tas de compilateurs et d'architectures que par défaut, tels que Intel C++ compiler) si f est dénormalisé.

Vous ne pouvez pas produire un dénormalisée flotter dans ce mode, mais le scénario est toujours possible:

a) Dénormalisée flotteur vient de la source externe.

b) Certaines bibliothèques trafiquer avec FPU modes, mais oublie (ou intentionnellement éviter) paramètre de retour après chaque appel de fonction, rendant possible pour les appelant à une inadéquation de la normalisation.

Exemple pratique qui imprime suivantes:

f = 5.87747e-39
f2 = 5.87747e-39

f = 5.87747e-39
f2 = 0
error, f != f2!

L'exemple fonctionne à la fois pour VC2010 et GCC 4.3, mais suppose que VC utilise de l'ESS pour les mathématiques en tant que par défaut et GCC utilise la FPU pour les mathématiques en tant que par défaut. L'exemple peut ne pas illustrer le problème autrement.

#include <limits>
#include <iostream>
#include <cmath>

#ifdef _MSC_VER
#include <xmmintrin.h>
#endif

template <class T>bool normal(T t)
{
    return (t != 0 || fabsf( t ) >= std::numeric_limits<T>::min());
}

void csr_flush_to_zero()
{
#ifdef _MSC_VER
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
#else
    unsigned csr = __builtin_ia32_stmxcsr();
    csr |= (1 << 15);
    __builtin_ia32_ldmxcsr(csr);
#endif
}

void test_cast(float f) 
{
    std::cout << "f = " << f << "\n";
    double d = double(f);
    float f2 = float(d);
    std::cout << "f2 = " << f2 << "\n";

    if(f != f2)
        std::cout << "error, f != f2!\n";

    std::cout << "\n";
}

int main()
{
    float f = std::numeric_limits<float>::min() / 2.0;

    test_cast(f);
    csr_flush_to_zero();
    test_cast(f);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X