Tous les nombres magiques créés sont-ils les mêmes?

77

Sur un projet récent, j'avais besoin de convertir d'octets en kilobytes kibibytes . Le code était assez simple:

var kBval = byteVal / 1024;

Après avoir écrit cela, j'ai fait fonctionner le reste de la fonction et je suis passé à autre chose.

Mais plus tard, j'ai commencé à me demander si je venais d'incorporer un nombre magique dans mon code. Une partie de moi dit que c'était bien parce que le nombre est une constante fixe et doit être facilement compris. Mais une autre partie de moi pense que cela aurait été super clair si elle était enveloppée dans une constante définie comme BYTES_PER_KBYTE.

Alors, les nombres qui sont des constantes bien connues sont-ils vraiment magiques ou non?


Questions connexes:

Quand un nombre est-il un nombre magique? et chaque numéro du code est-il considéré comme un "nombre magique"? - sont similaires, mais sont des questions beaucoup plus larges que ce que je demande. Ma question porte sur des nombres constants bien connus, qui ne sont pas abordés dans ces questions.

Éliminer les nombres magiques: quand est-il temps de dire "Non"? est également liée, mais est axée sur la refactorisation, par opposition à la question de savoir si un nombre constant est un nombre magique.

Communauté
la source
17
J'ai en fait travaillé sur un projet où ils avaient créé des constantes comme FOUR_HUNDRED_FOUR = 404. J'ai travaillé sur un autre projet où ils étaient militants pour utiliser des chaînes constantes au lieu de littéraux, ils avaient donc des dizaines de lignes de code qui ressemblaient àDATABASE = "database"
Rob
82
Utilisez certainement 1024, parce que sinon votre équipe de développement passera tout son temps à se disputer pour savoir si c'est "kilo-octets" ou "kibibytes".
Gort le robot
6
Vous pourriez considérer que 1024 est un kibi et #define KIBIque 1024, MEBI1024 * 1024…
jeudi
6
@Rob Y: ça sonne comme de bons vieux programmeurs Fortran. Parce que ce langage de programmation a forcé les programmeurs à le faire. Oui, vous verrez des constantes telles que, ZERO=0, ONE=1, TWO=2et lorsque les programmes sont portés dans d'autres langues (ou que les programmeurs ne changent pas de comportement lorsqu'ils changent de langue), vous le voyez aussi et vous devez prier pour que personne ne le change en ONE=2...
Holger
4
@NoctisSkytower Mon équipe préfère utiliser des instructions de division explicites plutôt que des opérateurs à décalage de bits, en raison des multiples langages que nous utilisons et de la mise en œuvre potentiellement incohérente dans ces langages. De même, les valeurs négatives sont traitées de manière incohérente avec un décalage au niveau du bit. Bien que nous n'ayons pas nécessairement des valeurs d'octet négatives, nous avons certainement des valeurs négatives avec d'autres unités de mesure que nous convertissons.

Réponses:

103

Tous les nombres magiques ne sont pas identiques.

Je pense que dans ce cas, cette constante est OK. Le problème avec les nombres magiques, c’est quand ils sont magiques, c’est-à-dire qu’on ne sait pas quelle est leur origine, pourquoi la valeur est ce qu’elle est, ou si la valeur est correcte ou non.

Se cacher 1024 derrière BYTES_PER_KBYTE signifie également que vous ne voyez pas instantanément si c'est correct ou non.

Je m'attendrais à ce que quiconque sache immédiatement pourquoi la valeur est 1024. Par contre, si vous convertissiez des octets en mégaoctets, je définirais la constante BYTES_PER_MBYTE ou similaire, car la constante 1 048 576 n'est pas si évidente que sa valeur 1024 ^ 2, ou que c'est même correct.

Il en va de même pour les valeurs dictées par des exigences ou des normes, qui ne sont utilisées qu’à un seul endroit. Je trouve qu'il est plus facile de traiter le droit constant en place avec un commentaire à la source pertinente que de le définir ailleurs et de devoir poursuivre les deux parties, par exemple:

// Value must be less than 3.5 volts according to spec blah.
SomeTest = DataSample < 3.50

Je trouve mieux que

SomeTest = DataSample < SOME_THRESHOLD_VALUE

À SOME_THRESHOLD_VALUEmon avis, ce n’est qu’à partir du moment où le compromis devient utile pour définir une constante.

comment s'appelle-t-il
la source
67
"Le problème avec les nombres magiques, c’est quand ils sont magiques" - C’est une explication si brillante de ce concept! Je suis sérieux! +1 pour cette phrase seulement.
Jörg W Mittag
20
En voici un que je viens de dire: "ce n'est pas le nombre qui pose problème, c'est la magie".
Jörg W Mittag
10
1024 est évident pour qui? N'est-ce pas la justification de chaque nombre magique? Tous les nombres magiques sont utilisés car ils sont évidents pour quiconque les a écrits. 9.8 n'est-il pas aussi évident? Pour moi, il est assez évident que ce soit l'accélération de la gravité sur Terre, mais je créerais néanmoins une constante, car ce qui est évident pour moi peut ne pas l'être pour quelqu'un d'autre.
Tulains Córdova
15
Non. Un commentaire comme celui de votre "meilleur" exemple est un drapeau rouge massif. C'est un code qui ne passe même pas le test de lisibilité de la personne qui l'écrit à ce moment-là. Je vais donner un exemple. e^i*pi = -1est beaucoup plus explicite (meilleur) que 2.718^i*3.142 = -1. Les variables sont importantes et elles ne concernent pas uniquement le code commun. Le code est écrit pour être lu en premier, puis en deuxième. En outre, les spécifications changent (beaucoup). Bien que le 1024 ne devrait probablement pas être dans la configuration, la version 3.5 sonne comme il se doit.
Nathan Cooper
51
Je ne voudrais pas utiliser une constante pour 1024 ^ 2 non plus; 1024*1024plz!
Courses de légèreté avec Monica
44

Je pose deux questions concernant les nombres magiques.

Le numéro a-t-il un nom?

Les noms sont utiles car nous pouvons lire le nom et comprendre l’utilité du numéro qui se trouve derrière. Les constantes de dénomination peuvent augmenter la lisibilité si le nom est plus facile à comprendre que le nombre qu'il remplace et si le nom de la constante est concis.

Clairement, des constantes telles que pi, e, et al. avoir des noms significatifs. Une valeur telle que 1024 pourrait être, BYTES_PER_KBmais je m'attendrais également à ce que tout développeur sache ce que 1024 signifie. Le public cible du code source est constitué de programmeurs professionnels qui devraient avoir l'expérience nécessaire pour connaître les différentes puissances des deux et la raison de leur utilisation.

Est-il utilisé dans plusieurs endroits?

Alors que les noms sont une force des constantes, une autre est la réutilisabilité. Si une valeur est susceptible de changer, elle peut être modifiée à un endroit au lieu de devoir la rechercher à plusieurs endroits.

Ta question

Dans le cas de votre question, j'utiliserais le numéro tel quel.

Nom: il y a un nom pour ce numéro, mais ce n'est pas vraiment utile. Il ne représente pas une constante ou une valeur mathématique spécifiée dans un document d'exigences.

Emplacements: même s’ils sont utilisés dans plusieurs endroits, ils ne changeront jamais, annulant ainsi cet avantage.


la source
1
La raison d'utiliser des constantes au lieu de nombres magiques n'est pas seulement due au fait que ces nombres vont changer, c'est aussi pour la lisibilité et l'auto-documentation.
Tulains Córdova
4
@ user61852: les constantes nommées ne sont pas toujours plus lisibles. Ils le sont souvent, mais pas toujours.
Whatsisname
2
Personnellement, j'utilise plutôt ces deux questions: "Cette valeur changera-t-elle jamais dans la durée de vie du programme?" et "Les développeurs auxquels je m'attendrais-ils travailler sur ce logiciel comprendront-ils le rôle de ce numéro?"
Gort le robot
4
Vous voulez dire le problème de l'an 2000? Je ne suis pas sûr que ce soit pertinent ici. Oui, il y avait beaucoup de code comme 'date - 1900', mais dans ce code, le problème n'était pas le nombre magique '1900'.
Gort le robot
1
Cette réponse pourrait tirer avantage d'une mention selon laquelle certains nombres "évidents", 1024 en étant un, sont tels que d'autres développeurs sont très susceptibles de les écrire spontanément sous forme de nombres, même lorsque quelqu'un définit une constante nommée pour eux. Pour ma part , le plus probable serait même pas penser à la recherche du code source constante existant pour 1024 si je ne l' ai pas déjà savoir il y a un, si je devais utiliser 1024 octets dans la conversion de quantité.
Hyde
27

Cette citation

Ce n'est pas le nombre qui pose problème, c'est la magie.

comme dit par Jörg W Mittag répond à cette question tout à fait bien.

Certains chiffres ne sont tout simplement pas magiques dans un contexte particulier. Dans l'exemple fourni dans la question, les unités de mesure étaient spécifiées par les noms de variables et l'opération en cours était parfaitement claire.

Ce 1024n’est donc pas magique, car le contexte indique très clairement que c’est la valeur constante et appropriée à utiliser pour les conversions.

De même, un exemple de:

var numDays = numHours / 24; 

est tout aussi clair et pas magique car il est bien connu qu'il y a 24 heures dans la journée.

Communauté
la source
21
Mais ... mais ... 24 peuvent changer! La Terre ralentit sa rotation et aura éventuellement 25 heures! (Bien sûr, nous serons tous morts d'ici là, ce qui fera de la maintenance de ce logiciel le problème de quelqu'un d'autre)
14
Que se passe-t-il après le déploiement de votre logiciel sur Mars? Tu devrais être en
train d'
8
@ durron597: que se passe-t-il si votre programme fonctionne suffisamment longtemps pour que la Terre ralentisse pendant ce temps ? Vous ne devriez pas injecter une constante, mais plutôt une fonction qui accepte un horodatage (par défaut maintenant) et renvoie le nombre d'heures dans la journée où l'horodatage tombe ;-)
Steve Jessop
13
Vous aurez besoin d'apprendre YAGNI.
Whatsisname
3
@ durron597 Rien de spécial ne se produit lorsque votre logiciel de chronométrage est déployé sur Mars car, selon la convention, les journées de Mars durent 24 heures mais chaque heure est 2,7% plus longue que sur Terre . Bien sûr, ni un jour sidéral de la Terre ni un jour solaire de la Terre ne sont exactement 24 heures (les chiffres exacts sont sur la même page), vous ne pouvez donc pas les utiliser 24 quand même! Comme mentionné par Izkata, les secondes intercalaires font mal. Peut-être auriez-vous plus de chance en utilisant la constante 24sur Mars que sur Terre!
un CVn
16

D'autres affiches ont mentionné que la conversion en cours est «évidente», mais je ne suis pas d'accord. La question initiale, à ce moment-ci, comprend:

kilobytes kibibytes

Donc, je sais déjà que l'auteur est ou était confus. La page Wikipedia ajoute à la confusion:

1000 = KB kilobyte (metric)
1024 = kB kilobyte (JEDEC)
1024 = KiB kibibyte (IEC)

Ainsi, «kilo-octet» peut être utilisé pour désigner à la fois un facteur de 1000 et 1024, la seule différence en abrégé étant la capitalisation du «k». De plus, 1024 peut signifier kilobyte (JEDEC) ou kibibyte (IEC). Pourquoi ne pas briser toute cette confusion avec une constante avec un nom significatif? En passant, ce fil de discussion a fréquemment utilisé "BYTES_PER_KBYTE", et ce n’est pas moins ambigu. KBYTE: est-ce KIBIBYTE ou KILOBYTE? Je préférerais ignorer JEDEC et avoir BYTES_PER_KILOBYTE = 1000et BYTES_PER_KIBIBYTE = 1024. Plus de confusion.

La raison pour laquelle des gens comme moi, et beaucoup d’autres, ont des opinions «militantes» (pour citer un commentateur ici) sur la désignation de nombres magiques est tout simplement de documenter ce que vous avez l’ intention de faire et de lever l’ambiguïté. Et vous avez en fait choisi une unité qui a conduit à beaucoup de confusion.

Si je vois:

int BYTES_PER_KIBIBYTE = 1024;  
...  
var kibibytes = bytes / BYTES_PER_KIBIBYTE;  

Ensuite, ce que l'auteur avait l'intention de faire est immédiatement évident et il n'y a aucune ambiguïté. Je peux vérifier la constante en quelques secondes (même si c'est dans un autre fichier), alors même si ce n'est pas «instantané», c'est assez proche d'instantané.

En fin de compte, cela peut paraître évident lorsque vous l'écrivez, mais ce le sera moins lorsque vous y reviendrez plus tard, et encore moins lorsque quelqu'un l'éditera. Il faut 10 secondes pour faire une constante; cela pourrait prendre une demi-heure ou plus pour résoudre un problème avec des unités (le code ne vous sautera pas dessus et vous dira que les unités sont fausses, vous devrez faire le calcul vous-même pour le résoudre, et vous aurez probablement chercher 10 avenues différentes avant de vérifier les unités).

Shaz
la source
2
Bonne réponse de compteur. Ce serait plus fort si vous preniez en compte la culture de chaque équipe. Si vous avez cru mon profil SE , je suis assez vieux pour précéder ces normes particulières. Donc, la seule confusion vient de "quel est le terme (non) standard actuel?" Et vous seriez probablement en sécurité en supposant que je travaille avec une équipe de dinosaures qui ont tous la même terminologie (non) difficile.
@ GlenH7: À mon humble avis, les unités basées sur la puissance de deux devraient être conservées pour le stockage, car elles sont allouées en blocs de taille double. Si la taille d'allocation minimale est de 4096 octets, est-il plus logique de disposer d'une unité correspondant à la quantité de stockage nécessaire pour contenir 256 fichiers de taille minimale, ou à la quantité de stockage nécessaire pour contenir 244.140625 fichiers de ce type? Personnellement, j’estime que la différence entre les mégaoctets de disques durs et d’autres mégaoctets est analogue à la différence entre les pouces diagonaux d’un poste de télévision et les pouces diagonaux réels.
Supercat
@Ryan: Pour ce cas particulier, je préférerais être militant pour l'adoption d'unités standard - Ko est 1000 octets ou le code est incorrect, et 1024 octets est KiB ou le code est incorrect. C'est la seule façon pour nous de surmonter le problème "les unités sont ambiguës". Différentes personnes définissant des "constantes magiques" (comme KB) différemment ne vont pas aider.
Brendan
11

Définir un nom comme faisant référence à une valeur numérique suggère que chaque fois qu'une valeur différente est requise dans un emplacement utilisant ce nom, elle le sera probablement dans tous les cas. Cela tend également à suggérer que la modification de la valeur numérique attribuée au nom est un moyen légitime de modifier la valeur. Une telle implication peut être utile quand c'est vrai et dangereuse quand c'est faux.

Le fait que deux endroits différents utilisent une valeur littérale particulière (par exemple, 1024) suggérera faiblement que les changements qui inciteraient un programmeur à en changer une sont quelque peu susceptibles d'inspirer le programmeur à vouloir en changer d'autres, mais cette implication est bien plus faible que ce qui serait appliqué si le programmeur a attribué un nom à une telle constante.

Un danger majeur avec quelque chose comme #define BYTES_PER_KBYTE 1024ceci est que cela pourrait suggérer à quelqu'un qui se trouve printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE));qu'un moyen sûr de faire en sorte que le code utilise des milliers d'octets serait de changer la #definedéclaration. Toutefois, une telle modification pourrait être désastreuse si, par exemple, un autre code non lié recevait la taille d’un objet en kilo-octets et utilisait cette constante pour allouer un tampon.

Il pourrait être raisonnable d’utiliser #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024et d’ #define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024attribuer un nom différent à chaque utilisation de la constante 1024, mais il en résulterait que de nombreux identifiants seraient définis et utilisés exactement une fois. En outre, dans de nombreux cas, il est plus facile de comprendre ce que signifie une valeur si on voit le code où il est utilisé, et il est aussi plus simple de savoir où code signifie si on voit les valeurs des constantes qui y sont utilisées. Si un littéral numérique n'est utilisé qu'une seule fois dans un but particulier, l'écrire à l'endroit où il est utilisé produira souvent un code plus compréhensible que de lui attribuer une étiquette à un endroit et d'utiliser sa valeur ailleurs.

supercat
la source
7

Je préférerais utiliser uniquement le nombre, mais je pense qu’un problème important n’a pas été soulevé: le même nombre peut signifier différentes choses dans différents contextes, ce qui peut compliquer le refactoring.

1024 est également le nombre de KiB par MiB. Supposons que nous utilisions 1024 pour représenter également ce calcul quelque part ou à plusieurs endroits et que nous devions maintenant le modifier pour calculer le Bio à la place. Changer la constante est plus facile qu'une recherche globale / remplacement où vous pouvez accidentellement changer le mauvais à certains endroits, ou le manquer à d'autres.

Ou ce pourrait même être un masque introduit par un programmeur paresseux qui doit être mis à jour un jour.

C'est un exemple un peu artificiel, mais dans certaines bases de code, cela peut poser des problèmes lors du refactoring ou de la mise à jour pour de nouvelles exigences. Pour ce cas particulier cependant, je ne considérerais pas le nombre simple comme une forme vraiment mauvaise, surtout si vous pouvez inclure le calcul dans une méthode de réutilisation, je le ferais probablement moi-même, mais je considérerais la constante plus "correcte".

Toutefois, si vous utilisez des constantes nommées, comme le dit supercat, il est important de déterminer si le contexte est également important et si vous avez besoin de plusieurs noms.

Nick P
la source