it-swarm.com.de

Sind alle magischen Zahlen gleich?

Bei einem kürzlich durchgeführten Projekt musste ich von Bytes nach konvertieren kilobyte Kibibyte . Der Code war einfach genug:

var kBval = byteVal / 1024;

Nachdem ich das geschrieben hatte, funktionierte der Rest der Funktion und ging weiter.

Aber später begann ich mich zu fragen, ob ich gerade ein magische Zahl in meinen Code eingebettet hatte. Ein Teil von mir sagt, dass es in Ordnung war, weil die Zahl eine feste Konstante ist und leicht zu verstehen ist. Aber ein anderer Teil von mir glaubt, es wäre super klar gewesen, wenn es in eine definierte Konstante wie BYTES_PER_KBYTE Eingehüllt worden wäre.

Sind Zahlen, die bekannte Konstanten sind, wirklich so magisch oder nicht?


Verwandte Fragen :

Wann ist eine Zahl eine magische Zahl? und Wird jede Zahl im Code als "magische Zahl" betrachtet? - sind ähnlich, aber viel umfassendere Fragen als das, was ich bin fragen. Meine Frage konzentriert sich auf bekannte konstante Zahlen, die in diesen Fragen nicht behandelt werden.

Eliminieren von magischen Zahlen: Wann ist es an der Zeit, "Nein" zu sagen? ist ebenfalls verwandt, konzentriert sich jedoch auf das Refactoring im Gegensatz dazu, ob eine konstante Zahl eine magische Zahl ist oder nicht.

78
user53019

Nicht alle magischen Zahlen sind gleich.

Ich denke in diesem Fall ist diese Konstante in Ordnung. Das Problem mit magischen Zahlen ist, wenn sie magisch sind, d. H. Es ist unklar, woher sie stammen, warum der Wert so ist, wie er ist, oder ob der Wert korrekt ist oder nicht.

Wenn Sie 1024 hinter BYTES_PER_KBYTE verstecken, sehen Sie auch nicht sofort, ob es korrekt ist oder nicht.

Ich würde erwarten, dass jeder sofort weiß, warum der Wert 1024 ist. Wenn Sie dagegen Bytes in Megabyte konvertieren, würde ich die Konstante BYTES_PER_MBYTE oder ähnliches definieren, da die Konstante 1.048.576 nicht so offensichtlich ist, dass sie 1024 ^ 2 oder dass es sogar richtig ist.

Gleiches gilt für Werte, die durch Anforderungen oder Standards vorgegeben sind und nur an einer Stelle verwendet werden. Ich finde es einfacher, die Konstante mit einem Kommentar zur relevanten Quelle richtig zu platzieren, als sie anderswo zu definieren und beide Teile zu verfolgen, z.

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

Ich finde besser als

SomeTest = DataSample < SOME_THRESHOLD_VALUE

Nur wenn SOME_THRESHOLD_VALUE wird an mehreren Stellen verwendet, lohnt sich der Kompromiss meiner Meinung nach, eine Konstante zu definieren.

104
whatsisname

Ich stelle zwei Fragen, wenn es um magische Zahlen geht.

Hat die Nummer einen Namen?

Namen sind nützlich, weil wir den Namen lesen und den Zweck der dahinter stehenden Nummer verstehen können. Namenskonstanten können die Lesbarkeit verbessern, wenn der Name leichter zu verstehen ist als die Zahl, die er ersetzt und Der konstante Name ist kurz.

Konstanten wie pi, e et al. bedeutungsvolle Namen haben. Ein Wert wie 1024 könnte BYTES_PER_KB Sein, aber ich würde auch erwarten, dass jeder Entwickler weiß, was 1024 bedeutet. Die Zielgruppe für den Quellcode sind professionelle Programmierer, die den Hintergrund haben sollten, um verschiedene Zweierpotenzen zu kennen und warum sie verwendet werden.

Wird es an mehreren Orten verwendet?

Während Namen eine Stärke von Konstanten sind, ist eine andere die Wiederverwendbarkeit. Wenn sich ein Wert wahrscheinlich ändert, kann er an einer Stelle geändert werden, anstatt ihn an mehreren Stellen suchen zu müssen.

Ihre Frage

Bei Ihrer Frage würde ich die Nummer unverändert verwenden.

Name: Es gibt einen Namen für diese Nummer, aber es ist nichts wirklich Nützliches. Es stellt keine mathematische Konstante oder einen mathematischen Wert dar, der in einem Anforderungsdokument angegeben ist.

Standorte: Selbst wenn er an mehreren Standorten verwendet wird, ändert er sich nie und negiert diesen Vorteil.

44
user22815

Dieses Zitat

Es ist nicht die Zahl, die das Problem ist, es ist die Magie.

wie gesagt von Jörg W Mittag beantwortet diese Frage recht gut.

Einige Zahlen sind in einem bestimmten Kontext einfach nicht magisch. In dem in der Frage angegebenen Beispiel wurden die Maßeinheiten durch die Variablennamen angegeben, und die Operation, die stattfand, war ziemlich klar.

Damit 1024 ist nicht magisch, weil der Kontext sehr deutlich macht, dass es der geeignete, konstante Wert für Conversions ist.

Ebenso ein Beispiel für:

var numDays = numHours / 24; 

ist ebenso klar und nicht magisch, weil bekannt ist, dass es 24 Stunden am Tag gibt.

27
user53019

Andere Plakate haben erwähnt, dass die Konvertierung "offensichtlich" ist, aber ich bin anderer Meinung. Die ursprüngliche Frage zu diesem Zeitpunkt umfasst:

kilobyte Kibibyte

Ich weiß also schon, dass der Autor verwirrt ist oder war. Die Wikipedia-Seite trägt zur Verwirrung bei:

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

"Kilobyte" kann also sowohl als Faktor 1000 als auch als Faktor 1024 verwendet werden, wobei der einzige Unterschied in der Kurzform die Großschreibung des 'k' ist. Darüber hinaus kann 1024 Kilobyte (JEDEC) oder Kibibyte (IEC) bedeuten. Warum nicht all diese Verwirrung mit einer Konstante mit einem aussagekräftigen Namen zerstören? Übrigens hat dieser Thread häufig "BYTES_PER_KBYTE" verwendet, und das ist nicht weniger zweideutig. KBYTE: Ist es KIBIBYTE oder KILOBYTE? Ich würde JEDEC lieber ignorieren und BYTES_PER_KILOBYTE = 1000 und BYTES_PER_KIBIBYTE = 1024. Keine Verwirrung mehr.

Der Grund, warum Leute wie ich und viele andere da draußen "militante" (um hier einen Kommentator zu zitieren) Meinungen über die Benennung magischer Zahlen haben, besteht darin, zu dokumentieren, was Sie beabsichtigen zu tun haben, und Mehrdeutigkeit beseitigen. Und Sie haben tatsächlich eine Einheit ausgewählt, die zu viel Verwirrung geführt hat.

Wenn ich sehe:

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

Dann ist sofort klar, was der Autor vorhatte, und es gibt keine Unklarheiten. Ich kann die Konstante in Sekundenschnelle überprüfen (auch wenn sie sich in einer anderen Datei befindet). Obwohl sie nicht "augenblicklich" ist, ist sie nah genug an augenblicklich.

Am Ende mag es offensichtlich sein, wenn Sie es schreiben, aber es wird weniger offensichtlich sein, wenn Sie später darauf zurückkommen, und es kann noch weniger offensichtlich sein, wenn jemand anderes es bearbeitet. Es dauert 10 Sekunden, um eine Konstante zu erstellen. Das Debuggen eines Problems mit Einheiten kann eine halbe Stunde oder länger dauern (der Code springt nicht auf Sie zu und sagt Ihnen, dass die Einheiten falsch sind. Sie müssen selbst rechnen, um das herauszufinden.) und Sie werden wahrscheinlich 10 verschiedene Wege suchen, bevor Sie Einheiten überprüfen).

16
Shaz

Wenn Sie einen Namen so definieren, dass er sich auf einen numerischen Wert bezieht, bedeutet dies, dass ein anderer Wert, der an einer Stelle benötigt wird, an der dieser Name verwendet wird, wahrscheinlich insgesamt benötigt wird. Es wird auch darauf hingewiesen, dass das Ändern des dem Namen zugewiesenen numerischen Werts eine legitime Möglichkeit zum Ändern des Werts ist. Eine solche Implikation kann nützlich sein, wenn sie wahr ist, und gefährlich, wenn sie falsch ist.

Die Tatsache, dass zwei verschiedene Stellen einen bestimmten Literalwert verwenden (z. B. 1024), deutet schwach darauf hin, dass Änderungen, die einen Programmierer dazu veranlassen würden, einen zu ändern, den Programmierer wahrscheinlich dazu inspirieren, andere ändern zu wollen, aber diese Implikation ist viel schwächer als zutreffend wenn der Programmierer einer solchen Konstante einen Namen zugewiesen hat.

Eine große Gefahr bei so etwas wie #define BYTES_PER_KBYTE 1024 Besteht darin, dass es jemandem, der auf printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE)); stößt, nahe legen könnte, dass ein sicherer Weg, den Code Tausende von Bytes verwenden zu lassen, darin besteht, den #define Aussage. Eine solche Änderung könnte jedoch katastrophal sein, wenn z. Ein anderer nicht verwandter Code empfängt die Größe eines Objekts in KB und verwendet diese Konstante, wenn ein Puffer dafür zugewiesen wird.

Es mag sinnvoll sein, #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024 Und #define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024 Zu verwenden und jedem unterschiedlichen Zweck, den die Konstante 1024 erfüllt, einen anderen Namen zuzuweisen. Dies würde jedoch dazu führen, dass viele Bezeichner genau einmal definiert und verwendet werden. Außerdem ist es in vielen Fällen am einfachsten zu verstehen, was ein Wert bedeutet, wenn man den Code dort sieht, wo er verwendet wird, und es ist am einfachsten herauszufinden, wo Code bedeutet, wenn man die Werte der darin verwendeten Konstanten sieht. Wenn ein numerisches Literal nur einmal für einen bestimmten Zweck verwendet wird, führt das Schreiben des Literals an der Stelle, an der es verwendet wird, häufig zu verständlicherem Code, als ihm an einer Stelle eine Bezeichnung zuzuweisen und seinen Wert an einer anderen Stelle zu verwenden.

11
supercat

Ich würde mich dazu neigen, nur die Nummer zu verwenden, aber ich denke, ein wichtiges Thema wurde nicht angesprochen: Dieselbe Nummer kann in verschiedenen Kontexten unterschiedliche Bedeutungen haben, was das Refactoring erschweren kann.

1024 ist auch die Anzahl von KiB pro MiB. Angenommen, wir verwenden 1024, um diese Berechnung auch irgendwo oder an mehreren Stellen darzustellen, und jetzt müssen wir sie ändern, um GiB stattdessen zu berechnen. Das Ändern der Konstante ist einfacher als ein globales Suchen/Ersetzen wo Sie können an einigen Stellen versehentlich die falsche ändern oder an anderen Stellen verpassen.

Oder es könnte sogar eine Bitmaske sein, die von einem faulen Programmierer eingeführt wurde und eines Tages aktualisiert werden muss.

Es ist ein etwas erfundenes Beispiel, aber in einigen Codebasen kann dies zu Problemen beim Refactoring oder Aktualisieren für neue Anforderungen führen. Für diesen speziellen Fall würde ich die einfache Zahl jedoch nicht als wirklich schlechte Form betrachten, insbesondere wenn Sie die Berechnung in eine Methode zur Wiederverwendung einschließen können. Ich würde es wahrscheinlich selbst tun, aber die Konstante als "korrekter" betrachten.

Wenn Sie jedoch benannte Konstanten verwenden, ist es, wie Supercat sagt, wichtig zu prüfen, ob auch der Kontext wichtig ist und ob Sie mehrere Namen benötigen.

7
Nick P