Zéro comme constante?

15

J'ai rencontré cet idiome de programmation récemment:

const float Zero = 0.0;

qui est ensuite utilisé dans les comparaisons:

if (x > Zero) {..}

Quelqu'un peut-il expliquer si cela est vraiment plus efficace ou plus lisible ou maintenable que:

if (x > 0.0) {..}

NOTE: Je peux penser à d' autres raisons pour définir cette constante, je me demande simplement son utilisation dans ce contexte.

NWS
la source
31
Les développeurs envisagent de porter le code dans un univers où les lois des mathématiques sont différentes?
vaughandroid
6
Sérieusement, je ne peux pas penser à une seule bonne raison à cela. Les seules explications que je peux trouver sont des normes de codage trop zélées, ou certains développeurs qui ont entendu "les nombres magiques sont mauvais" mais ne comprennent pas pourquoi (ou ce qui constituerait un nombre magique) ...
vaughandroid
@Baqueta -Un univers alternatif? Je pense qu'ils y vivaient déjà! En ce qui concerne les nombres magiques, je suis d'accord, mais j'utilise la règle générale que tout sauf 0 et 1 doit être rendu constant.
NWS
1
Si xa le type float, x > 0.0force la promotion vers double, ce qui pourrait être moins efficace. Ce n'est pas une bonne raison d'utiliser une constante nommée, juste pour vous assurer que vos constantes ont le bon type (par exemple 0f, float(0)ou decltype(x)(0)).
Mike Seymour
1
@JoSo: pour être juste, le type 13.37n'est pas float, ça l'est double. Donc, si vous en vouliez un, floatil est concevable que votre tuteur ait eu raison. Dans certains contextes (par exemple, l'affectation à un flottant) 13.37sera implicitement converti en celui floatque vous vouliez, et dans d'autres contextes (par exemple, la déduction de type de modèle), il ne le sera pas, tandis que le static const floatcommence toujours comme le type que vous vouliez. Par conséquent, plus sûr pour le type. Attention, ce serait le cas 13.37f! Il existe d'autres raisons pour éviter la macro que la "sécurité de type", donc il est tout aussi probable que le tuteur vous donne un mauvais argument.
Steve Jessop

Réponses:

29

Les raisons possibles sont la mise en cache, la dénomination ou le forçage du type

Mise en cache (non applicable)

Vous voulez éviter le coût de création d'un objet lors de l'acte de comparaison. En Java, un exemple serait

BigDecimal zero = new BigDecimal ("0.0");

cela implique un processus de création assez lourd et est mieux servi en utilisant la méthode statique fournie:

BigDecimal zero = BigDecimal.ZERO;

Cela permet des comparaisons sans encourir de coûts de création répétés car le BigDecimal est pré-mis en cache par la JVM lors de l'initialisation.

Dans le cas de ce que vous avez décrit, une primitive effectue le même travail. Ceci est largement redondant en termes de mise en cache et de performances.

Dénomination (peu probable)

Le développeur d'origine tente de fournir une convention de dénomination uniforme pour les valeurs communes dans tout le système. Cela a un certain mérite, en particulier avec des valeurs rares, mais quelque chose d'aussi basique que zéro n'en vaut la peine que dans le cas du cas de mise en cache plus tôt.

Type de forçage (le plus probable)

Le développeur d'origine tente de forcer un type primitif particulier pour garantir que les comparaisons sont converties en leur type correct et éventuellement à une échelle particulière (nombre de décimales). C'est OK, mais le simple nom "zéro" est probablement un détail insuffisant pour ce cas d'utilisation avec ZERO_1DP étant une expression plus appropriée de l'intention.

Gary Rowe
la source
2
+1 pour forcer le type. J'ajouterai que dans des langages comme C ++ qui permettent la surcharge des opérateurs, la définition d'une constante et l'utilisation de typedefgarderaient le type de la variable à un seul endroit et permettraient de le changer sans avoir à modifier le code.
Blrfl
3
Forcer le type n'est probablement pas ce pour quoi ils essayaient, mais c'est la meilleure explication pour laquelle cela pourrait être fait!
NWS
7
Pour forcer le type, je préfère probablement simplement utiliser 0.0f.
Svish
Forcer le type peut parfois être utile dans vb.net, où l'exécution d'opérateurs au niveau du bit sur les octets donne un résultat en octets. Dire byteVar1 = byteVar2 Or CB128semble un peu plus agréable que byteVar1 = byteVar2 Or CByte(128). Bien sûr, il serait préférable d'avoir un suffixe numérique approprié pour les octets. Étant donné que C # promeut les opérandes de bit à bit aux opérateurs intmême lorsque le résultat serait garanti pour tenir dans un byte, le problème n'est pas si pertinent là-bas.
supercat
Je ne suis pas sûr d'avoir le nom constant comme zéro pour '0' mais parfois cela aide à la lisibilité du code; par exemple, avoir cette constante pour zéro - "ROOT_TYPE_ID = 0" aidera à écrire une instruction comme if (id! = ROOT_TYPE_ID) {..}
Tech Junkie
6

C'est à cause de "l'outillage lancinant"

Une raison possible que je ne vois pas ici est parce que beaucoup d'outils de qualité signalent l'utilisation de nombres magiques . C'est souvent une mauvaise pratique que de placer des nombres magiques dans un algorithme sans les rendre clairement visibles pour les modifications ultérieures, surtout s'ils sont dupliqués à plusieurs endroits dans le code.

Ainsi, bien que ces outils aient raison de signaler de tels problèmes, ils génèrent souvent des faux positifs pour les situations où ces valeurs sont inoffensives et très probablement statiques, ou simplement des valeurs d'initialisation.

Et lorsque cela se produit, vous êtes parfois confronté au choix de:

  • les marquer comme faux positifs, si l'outil le permet (généralement avec un commentaire spécialement formaté, ce qui est gênant pour les personnes n'utilisant pas l'outil)
  • ou extraire ces valeurs en constantes, que cela soit important ou non.

À propos des performances

Cela dépend du langage, je suppose, mais cela est assez courant en Java et n'a aucun impact sur les performances, car les valeurs sont alignées au moment de la compilation s'il s'agit de constantes réelles static final. Cela n'aurait pas d'impact en C ou C ++ s'ils sont déclarés comme constantes ou même comme macros de pré-processeur.

haylem
la source
5

Cela pourrait avoir un sens car il définit explicitement Zerocomme étant de type float.

Au moins en C et C ++, la valeur 0.0est de type double, tandis que l'équivalent l' floatest 0.0f. Donc, supposer que xvous vous comparez est également toujours un floatdicton

x > 0.0

tout en faisant la promotion xde doublepour correspondre au type de 0.0ce qui pourrait entraîner des problèmes (avec des tests d'égalité en particulier). La comparaison sans conversion serait bien entendu

x > 0.0f

qui fait la même chose que

float Zero = 0.0; // double 0.0 converted to float  
x > Zero

Néanmoins, je pense qu'il serait beaucoup plus utile d'activer les avertissements de conversions dans le compilateur au lieu de demander aux utilisateurs d'écrire du code gênant.

Benjamin Bannier
la source
1

Tout d'abord, ici zéro est défini comme floatnon int. Bien sûr, cela n'affecte rien dans la comparaison, mais dans d'autres cas, lorsque cette constante est utilisée, cela peut faire la différence.

Je ne vois aucune autre raison pour laquelle Zeroest déclaré une constante ici. C'est juste un style de codage, et il est préférable de suivre le style s'il est utilisé partout ailleurs dans ce programme.

superM
la source
1

Il est presque certainement aussi efficace lors de l'exécution (sauf si votre compilateur est très primitif) et très légèrement moins efficace lors de la compilation.

Quant à savoir si c'est plus lisible que x > 0... rappelez-vous qu'il y a des gens qui honnêtement, vraiment, pensent que COBOL était une excellente idée et un plaisir de travailler avec - et puis il y a des gens qui pensent exactement la même chose à propos de C. (La rumeur a il existe même des programmeurs ayant la même opinion sur C ++!) En d'autres termes, vous n'obtiendrez pas d'accord général sur ce point, et cela ne vaut probablement pas la peine de se battre.

Kilian Foth
la source
0

[est] c'est vraiment plus efficace ou plus lisible ou maintenable que:

if (x > 0.0) {..}

Si vous écriviez du code générique (c'est-à-dire non spécifique au type), alors très probablement. Une zero()fonction peut s'appliquer à tout type algébrique ou à tout type qui est un ajout par groupe. Ce pourrait être un entier, ce pourrait être une valeur à virgule flottante, ce pourrait même être une fonction si votre variable est, par exemple, elle-même une fonction dans un espace linéaire (par exemple x est une fonction linéaire de la forme z -> a_x * z + b_x) et zero()fournit ensuite la fonction avec a et b étant tous deux zero()du type sous-jacent.

Donc, vous vous attendez à un tel code dans, disons, C ++ éventuellement (bien que a zero()ne soit pas très courant AFAIK), ou dans Julia, et peut-être dans d'autres langages.

einpoklum
la source