86 votes

Est 'float a = 3.0; une déclaration appropriée?

Si j'ai la déclaration suivante:

float a = 3.0 ;

est qu'une erreur? J'ai lu dans un livre qu' 3.0 est double de la valeur et que je dois spécifier que c' float a = 3.0f. Est-ce donc?

159voto

quantdev Points 15386

Ce n'est pas une erreur de déclarer float a = 3.0 : si vous le faites, le compilateur se charge de convertir le double littérale 3.0 à un flotteur pour vous.


Cependant, vous devez utiliser le flotteur littéraux de notation dans des scénarios spécifiques.

  1. Pour des raisons de performances:

    Plus précisément, pensez à:

    float foo(float x) { return x * 0.42; }
    

    Ici, le compilateur émet un conversion (que vous aurez à payer lors de l'exécution) pour chaque valeur de retour. Pour éviter cela, vous devez déclarer:

    float foo(float x) { return x * 0.42f; } // OK, no conversion required
    
  2. Pour éviter des bugs lorsque l'on compare les résultats:

    par exemple, la comparaison suivante échoue :

    float x = 4.2;
    if (x == 4.2)
       std::cout << "oops"; // Not executed!
    

    On peut la fixer avec le flotteur de la notation littérale :

    if (x == 4.2f)
       std::cout << "ok !"; // Executed!
    

    (Note: bien sûr, ce n'est pas la façon dont vous devriez comparer float ou double des chiffres de l'égalité en général)

  3. Pour appeler la bonne fonction surchargée (pour la même raison):

    Exemple:

    void foo(float f) { std::cout << "\nfloat"; }
    
    void foo(double d) { std::cout << "\ndouble"; }
    
    int main()
    {       
        foo(42.0);   // calls double overload
        foo(42.0f);  // calls float overload
        return 0;
    }
    
  4. Comme l'a noté le Cyber, dans un type de déduction de contexte, il est nécessaire d'aider le compilateur en déduire une float :

    Dans le cas d' auto :

    auto d = 3;      // int
    auto e = 3.0;    // double
    auto f = 3.0f;   // float
    

    Et de même, dans le cas du modèle, le type de déduction :

    void foo(float f) { std::cout << "\nfloat"; }
    
    void foo(double d) { std::cout << "\ndouble"; }
    
    template<typename T>
    void bar(T t)
    {
          foo(t);
    }
    
    int main()
    {   
        bar(42.0);   // Deduce double
        bar(42.0f);  // Deduce float
    
        return 0;
    }
    

Démonstration en direct

22voto

Cyber Points 15779

Le compilateur va tourner tout de suite à des littéraux dans les chars, parce que vous avez déclaré la variable comme un float.

float a = 3;     // converted to float
float b = 3.0;   // converted to float
float c = 3.0f;  // float

Il serait question est de savoir si vous avez utilisé auto (ou autre type de déduction de méthodes), par exemple:

auto d = 3;      // int
auto e = 3.0;    // double
auto f = 3.0f;   // float

14voto

Shafik Yaghmour Points 42198

Virgule flottante littéraux sans suffixe sont de type double, ce qui est couvert dans le projet de norme C++ section 2.14.4 Flottant littéraux:

[...]Le type de flottant littérale est double sauf s'il est explicitement spécifié par un suffixe.[...]

ainsi est-il une erreur d'attribuer 3.0 un double littérale d'un flotteur?:

float a = 3.0

Non, il n'est pas, il sera converti, qui est couvert dans la section 4.8 virgule Flottante conversions:

Un prvalue de type à virgule flottante peuvent être convertis à un prvalue de un autre type à virgule flottante. Si la valeur de la source peut être exactement représenté dans le type de destination, le résultat de la conversion est cette représentation exacte. Si la valeur de la source est entre les deux adjacentes destination valeurs, le résultat de la conversion est un la mise en œuvre définies par le choix de l'une de ces valeurs. Sinon, le comportement est indéfini.

On peut lire plus de détails sur les implications de ce à GotW #67: chambre double ou rien qui dit:

Cela signifie qu'une double constante peut être implicite (c'est à dire, silencieusement) converti en float constante, même si cela perd en précision (c'est à dire, de données). Cela a été autorisé à rester pour C de la compatibilité et de la convivialité des raisons, mais il est bon de garder à l'esprit lorsque vous ne vous en virgule flottante travail.

Une qualité compilateur vous avertira si vous essayez de faire quelque chose de un comportement indéfini, à savoir mettre une double quantité dans un flotteur qui est moins que le minimum, ou supérieure à la valeur maximale, la valeur d'un flotteur est capable de représenter. Vraiment un bon compilateur va fournir une option de avertissement si vous essayez de faire quelque chose qui peut être défini, mais il pourrait perdre de l'information, à savoir mettre une double quantité dans un flotteur qui est entre les valeurs minimale et maximale représentable par un flotteur, mais qui ne peut pas être représenté exactement comme un float.

Il y a donc des réserves pour le cas général que vous devez être conscients de.

À partir d'un point de vue pratique, dans ce cas, les résultats seront les plus susceptibles d'être la même, même si, techniquement, il y a une conversion, nous pouvons voir cela en essayant le code suivant sur godbolt:

#include <iostream>

float func1()
{
  return 3.0; // a double literal
}


float func2()
{
  return 3.0f ; // a float literal
}

int main()
{  
  std::cout << func1() << ":" << func2() << std::endl ;
  return 0;
}

et nous voyons que les résultats pour l' func1 et func2 sont identiques, à l'aide d' clang et gcc:

func1():
    movss   xmm0, DWORD PTR .LC0[rip]
    ret
func2():
    movss   xmm0, DWORD PTR .LC0[rip]
    ret

Comme Pascal le souligne dans ce commentaire, vous ne serez pas toujours être en mesure de compter sur cela. À l'aide de 0.1 et 0.1f respectivement provoque l'assemblée générés à différents depuis la conversion doit maintenant être fait de manière explicite. Le code suivant:

float func1(float x )
{
  return x*0.1; // a double literal
}

float func2(float x)
{
  return x*0.1f ; // a float literal
}

les résultats dans la suite de l'assemblée:

func1(float):  
    cvtss2sd    %xmm0, %xmm0    # x, D.31147    
    mulsd   .LC0(%rip), %xmm0   #, D.31147
    cvtsd2ss    %xmm0, %xmm0    # D.31147, D.31148
    ret
func2(float):
    mulss   .LC2(%rip), %xmm0   #, D.31155
    ret

Indépendamment de savoir si vous pouvez déterminer si la conversion n'aura un impact sur les performances ou non, en utilisant le bon type de meilleurs documents de votre intention. À l'aide d'un des conversions explicites par exemple static_cast aide également à expliquer la conversion a été conçu comme une opposition à accidentelle, ce qui peut signifier un bug ou bogue potentiel.

Note

Comme supercat points, la multiplication, par exemple en 0.1 et 0.1f n'est pas équivalent. Je vais juste citer le commentaire, car il était excellent et un résumé ne serait probablement pas faire la justice:

Par exemple, si f est égal à 100000224 (ce qui est exactement représentable comme un float), en le multipliant par un dixième devraient déboucher sur une résultat qui arrondit à 10000022, mais multiplier par 0,1 f au lieu d'obtenir un résultat qui, à tort, des tours jusqu'à 10000023. Si l' l'intention est de diviser par dix, la multiplication par un double constante de 0,1 sera probablement plus rapide que la division par 10, et plus précis que la multiplication par 0,1 f.

Mon point de départ était de faire une fausse exemple donné dans une autre question, mais cette finement montre de questions subtiles peuvent exister dans le jouet des exemples.

4voto

hvd Points 42125

Ce n'est pas une erreur dans le sens que le compilateur va le rejeter, mais c'est une erreur dans le sens qu'il peut ne pas être ce que vous voulez.

Que votre livre correctement, 3.0 est une valeur de type double. Il y a une conversion implicite de double de float, alors float a = 3.0; est valable définition d'une variable.

Cependant, au moins sur le plan conceptuel, elle effectue une inutile de conversion. Selon le compilateur, la conversion peut être effectuée au moment de la compilation, ou il peut être sauvé pour le moment de l'exécution. Une raison valable pour l'enregistrer pour le temps d'exécution est qu'à virgule flottante conversions sont difficiles et peuvent avoir des effets secondaires inattendus si la valeur ne peut pas être représenté exactement, et il n'est pas toujours facile de vérifier si la valeur peut être représentée exactement.

3.0f évite ce problème: bien que techniquement, le compilateur est encore permis de calculer la constante au moment de l'exécution (c'est toujours le cas), ici, il n'y a absolument aucune raison pour qu'un compilateur peut éventuellement le faire.

2voto

SiteNook Points 60

Alors que pas une erreur en soi, c'est un peu bâclé. Vous savez que vous voulez un flotteur, afin de l'initialiser avec un flotteur.
Un autre programmeur peut venir et ne pas être sûr de la partie de la déclaration est correcte, le type ou l'initialiseur. Pourquoi ne pas les avoir à la fois correct?
flotteur de Réponse = 42.0 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