Sind alle magischen Zahlen gleich?

77

Bei einem kürzlich durchgeführten Projekt musste ich Kibibyte von Bytes in Kilobytes konvertieren . 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 eine magische Zahl in meinen Code eingebettet hatte . Ein Teil von mir sagt, es sei in Ordnung, weil die Zahl eine feste Konstante ist und leicht zu verstehen ist. Aber ein anderer Teil von mir denkt, dass es super klar gewesen wäre, wenn man es in eine definierte Konstante wie gewickelt hätte BYTES_PER_KBYTE.

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 es gibt viel umfassendere Fragen als das, was ich stelle. Meine Frage konzentriert sich auf bekannte konstante Zahlen, die in diesen Fragen nicht angesprochen werden.

Eliminierung magischer Zahlen: Wann ist es Zeit, "Nein" zu sagen? ist ebenfalls verwandt, konzentriert sich jedoch auf das Refactoring und nicht darauf, ob eine konstante Zahl eine magische Zahl ist oder nicht.

Gemeinschaft
quelle
17
Ich habe tatsächlich an einem Projekt gearbeitet, in dem Konstanten wie, erstellt wurden FOUR_HUNDRED_FOUR = 404. Ich habe an einem anderen Projekt gearbeitet, bei dem sie gegen die Verwendung von Konstanten anstelle von Literalen kämpften. Sie hatten also Dutzende von Codezeilen, die so aussahenDATABASE = "database"
Rob,
82
Verwenden Sie es auf jeden Fall 1024, da Ihr Entwicklerteam sonst die ganze Zeit damit verbringt, sich darüber zu streiten, ob es sich um "Kilobyte" oder "Kibibyte" handelt.
Steven Burnap
6
Sie könnten annehmen, dass 1024 Kibi und #define KIBI1024 ist, MEBIals 1024 * 1024…
ysdx
6
@ Rob Y: Klingt nach guten alten Fortran-Programmierern. Weil diese Programmiersprache Programmierer dazu gezwungen hat. Ja, es wird Sie Konstanten sehen , wie ZERO=0, ONE=1, TWO=2und wenn Programme in anderen Sprachen portiert werden (oder die Programmierer nicht Verhalten ändern , wenn ihre Sprachumschaltung) Sie es dort sehen werden , und Sie müssen beten , dass jemand es nie ändern ONE=2...
Holger
4
@NoctisSkytower Mein Team bevorzugt die Verwendung expliziter Divisionsanweisungen anstelle von Bitverschiebungsoperatoren, da wir mehrere Sprachen verwenden und die Implementierung in diesen Sprachen möglicherweise inkonsistent ist. Ebenso werden negative Werte bei bitweiser Verschiebung inkonsistent behandelt. Während wir nicht unbedingt negative Bytewerte haben müssen, haben wir mit Sicherheit negative Werte mit anderen Maßeinheiten, die wir konvertieren.

Antworten:

103

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, dh es ist unklar, woher sie stammen, warum der Wert so ist, wie er ist oder ob der Wert korrekt ist oder nicht.

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

Ich würde erwarten, dass jeder sofort weiß, warum der Wert 1024 ist. Andererseits würde ich, wenn Sie Bytes in Megabytes konvertieren, die Konstante BYTES_PER_MBYTE oder ähnliches definieren, da die Konstante 1.048.576 nicht so offensichtlich ist, dass sie 1024 ^ 2 ist, 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 zu der entsprechenden Quelle in Position zu bringen, als sie anderswo zu definieren und beide Teile zu verfolgen, zB:

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

Finde ich besser als

SomeTest = DataSample < SOME_THRESHOLD_VALUE

Nur wenn SOME_THRESHOLD_VALUEes an mehreren Stellen verwendet wird, lohnt es sich meiner Meinung nach, eine Konstante zu definieren.

Whatsisname
quelle
67
„Das Problem mit magischen Zahlen ist , wenn sie Magie“ - Dies ist solch eine brillante Erklärung dieses Konzepts! Ich meine es ernst! +1 für diesen Satz allein.
Jörg W Mittag
20
Hier ist eine, die ich mir gerade ausgedacht habe: "Es ist nicht die Zahl, die das Problem ist, es ist die Magie."
Jörg W Mittag
10
1024 ist für wen offensichtlich? Ist das nicht die Rechtfertigung für jede magische Zahl? Alle magischen Zahlen werden verwendet, weil sie für jeden, der sie geschrieben hat, offensichtlich sind. Ist 9.8 nicht auch offensichtlich? Für mich ist es ziemlich offensichtlich, dass die Schwerkraft auf der Erde beschleunigt wird, aber ich würde trotzdem eine Konstante schaffen, weil das, was für mich offensichtlich ist, für andere vielleicht nicht offensichtlich ist.
Tulains Córdova
15
Nein. Ein Kommentar wie der in Ihrem "besseren" Beispiel ist eine massive rote Fahne. Es ist Code, der nicht einmal den Lesbarkeitstest der Person besteht, die ihn gerade schreibt. Ich werde ein Beispiel geben. e^i*pi = -1ist weit expliziter (besser) als 2.718^i*3.142 = -1. Variablen sind wichtig und nicht nur für allgemeinen Code gedacht. Code wird zuerst zum Lesen geschrieben, dann zum Kompilieren. Auch die technischen Daten ändern sich (stark). Während die 1024 wahrscheinlich nicht in der Konfiguration sein sollte, klingt die 3.5 so, wie es sein sollte.
Nathan Cooper
51
Ich würde auch keine Konstante für 1024 ^ 2 verwenden. 1024*1024PLZ!
Leichtigkeitsrennen im Orbit
44

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. Benennungskonstanten können die Lesbarkeit verbessern, wenn der Name leichter zu verstehen ist als die Zahl, die er ersetzt, und der Name der Konstanten präzise ist.

Konstanten wie pi, e et al. haben aussagekräftige Namen. Ein Wert wie 1024 könnte sein, BYTES_PER_KBaber 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, die verschiedenen Potenzen von zwei 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 einem Ort geändert werden, anstatt ihn an mehreren Orten suchen zu müssen.

Ihre Frage

Bei Ihrer Frage würde ich die Nummer so verwenden, wie sie ist.

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: Auch wenn es an mehreren Standorten verwendet wird, ändert es sich nie, wodurch dieser Vorteil zunichte gemacht wird.


quelle
1
Der Grund für die Verwendung von Konstanten anstelle von magischen Zahlen liegt nicht nur darin, dass sich diese ändern, sondern auch in Bezug auf die Lesbarkeit und Selbstdokumentation.
Tulains Córdova
4
@ user61852: Benannte Konstanten sind nicht immer besser lesbar. Sie sind es oft, aber nicht immer.
Whatsisname
2
Persönlich verwende ich stattdessen diese beiden Fragen: "Wird sich dieser Wert im Laufe der Programmlaufzeit jemals ändern?" und "Würden die Entwickler, von denen ich erwarte, dass sie an dieser Software arbeiten, verstehen, wofür diese Nummer ist?"
Steven Burnap
4
Du meinst das Y2K Problem? Ich bin mir nicht sicher, ob es hier relevant ist. Ja, es gab eine Menge Code wie 'date - 1900', aber in diesem Code war das Problem nicht die magische Zahl "1900".
Steven Burnap
1
Diese Antwort könnte von einer Erwähnung profitieren, dass einige "offensichtliche" Zahlen, 1024 definitiv eine, so sind, dass andere Entwickler sie sehr wahrscheinlich spontan als Zahlen schreiben, selbst wenn jemand eine benannte Konstante für sie definiert. Ich für meinen Teil würde höchstwahrscheinlich nicht einmal daran denken, den Quellcode nach existierenden Konstanten für 1024 zu durchsuchen, wenn ich nicht bereits wüsste, dass es eine gibt, wenn ich 1024 in der Byte-Mengenumrechnung verwenden müsste.
Hyde
27

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 ganz 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 durchgeführte Operation war ziemlich klar.

Das 1024ist also nicht magisch, da der Kontext sehr deutlich macht, dass dies der geeignete, konstante Wert für Conversions ist.

Ebenso ein Beispiel für:

var numDays = numHours / 24; 

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

Gemeinschaft
quelle
21
Aber ... aber ... 24 kann sich ändern! Die Erde verlangsamt ihre Rotation und wird schließlich 25 Stunden haben! (Natürlich sind wir bis dahin alle tot und
14
Was passiert nach der Bereitstellung Ihrer Software auf dem Mars? Sie sollten diese Konstante injizieren ...
Durron597
8
@ durron597: Was , wenn Ihr Programm läuft lange genug , um die Erde zu verlangsamen während dieser Zeit . Sie sollten keine Konstante injizieren, sondern eine Funktion, die einen Zeitstempel akzeptiert (standardmäßig jetzt) ​​und die Anzahl der Stunden an dem Tag zurückgibt, an dem der Zeitstempel fällt ;-)
Steve Jessop
13
Du musst YAGNI lernen.
Whatsisname
3
@ durron597 Wenn Ihre Zeitnehmungssoftware auf dem Mars bereitgestellt wird, geschieht nichts Besonderes, da Mars-Tage normalerweise 24 Stunden lang sind, aber jede Stunde 2,7% länger als auf der Erde . Natürlich ist weder ein Sternentag auf der Erde noch ein Sonnentag auf der Erde genau 24 Stunden (die genauen Zahlen sind auf derselben Seite angegeben), Sie können sie also 24 sowieso nicht verwenden ! Wie Izkata erwähnte, taten Schaltsekunden weh. Vielleicht hättest du besseres Glück, wenn du die Konstante 24auf dem Mars benutzt als auf der Erde!
ein Lebenslauf vom
16

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 fügt der Verwirrung hinzu:

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 die ganze Verwirrung mit einer Konstante mit einem aussagekräftigen Namen auflösen? Ü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 es vorziehen JEDEC zu ignorieren und haben BYTES_PER_KILOBYTE = 1000und 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 zum Benennen von magischen Zahlen haben, besteht darin, zu dokumentieren, was Sie vorhaben , und Mehrdeutigkeiten zu 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), sodass sie zwar nicht "augenblicklich" ist, aber nahe genug ist, um augenblicklich zu sein.

Am Ende könnte es offensichtlich sein, wenn Sie es schreiben, aber es wird weniger offensichtlich sein, wenn Sie später darauf zurückkommen, und es könnte noch weniger offensichtlich sein, wenn jemand anderes es bearbeitet. Es dauert 10 Sekunden, um eine Konstante zu erstellen. Es kann eine halbe Stunde oder länger dauern, bis ein Problem mit Units behoben ist (der Code wird Sie nicht ansprechen und Ihnen mitteilen, dass die Units falsch sind. und du wirst wahrscheinlich 10 verschiedene Alleen suchen, bevor du Einheiten überprüfst).

Shaz
quelle
2
Gute Gegenantwort. Stärker wäre es, wenn Sie die individuelle Teamkultur berücksichtigen würden. Wenn Sie an mein SE-Profil geglaubt haben , bin ich alt genug, um diese besonderen Standards zu übertreffen. Die einzige Verwirrung ergibt sich aus "Was ist der aktuelle (Nicht-) Standardbegriff?" Und Sie sind wahrscheinlich auf der sicheren Seite, wenn Sie davon ausgehen, dass ich mit einem Team von Dinosaurierkollegen zusammenarbeite, die alle die gleichen terminologischen (Nicht-) Schwierigkeiten haben.
@ GlenH7: IMHO, die Power-of-Two-basierten Einheiten sollten für die Speicherung aufbewahrt werden, da sie in Power-of-Two-Chunks aufgeteilt sind. Wenn die minimale Zuordnungsgröße 4096 Byte beträgt, ist es sinnvoller, eine Einheit für die Speicherkapazität für 256 Dateien mit minimaler Größe oder die Speicherkapazität für 244.140625 solcher Dateien zu haben? Persönlich betrachte ich den Unterschied zwischen Festplattenhersteller-Megabyte und anderen Megabyte als analog zum Unterschied zwischen Fernsehgerät-Diagonalen und echten Diagonalen.
Supercat
@Ryan: In diesem speziellen Fall würde ich die Einführung von Standardeinheiten eher ablehnen - KB ist 1000 Bytes oder der Code ist falsch, und 1024 Bytes sind KiB oder der Code ist falsch. Dies ist der einzige Weg, wie wir jemals an dem Problem "Einheiten sind mehrdeutig" vorbeikommen. Verschiedene Leute, die "magische Konstanten" (wie KB) unterschiedlich definieren, werden nicht helfen.
Brendan
11

Wenn Sie einen Namen so definieren, dass er sich auf einen numerischen Wert bezieht, bedeutet dies, dass ein anderer Wert an einer Stelle, an der dieser Name verwendet wird, wahrscheinlich in allen Fällen benötigt wird. Es besteht auch die Tendenz, dass das Ändern des dem Namen zugewiesenen numerischen Werts eine legitime Möglichkeit zum Ändern des Werts darstellt. 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 anregen, andere zu ändern, aber diese Implikation ist viel schwächer als zutreffend wenn der Programmierer einer solchen Konstante einen Namen gegeben hat.

Eine große Gefahr bei so etwas #define BYTES_PER_KBYTE 1024ist, dass es für jemanden, der darauf stößt, printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE));dass ein sicherer Weg, den Code Tausende von Bytes verwenden zu lassen, darin besteht, die #defineAnweisung zu ändern . Eine solche Änderung könnte jedoch verheerend sein, wenn z. B. ein anderer nicht verwandter Code die Größe eines Objekts in KB empfängt und diese Konstante bei der Zuweisung eines Puffers verwendet.

Es kann sinnvoll sein, für jeden Zweck, den die Konstante 1024 erfüllt, einen anderen Namen zu verwenden #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024und #define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024zuzuweisen. 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 am einfachsten herauszufinden, wo Code bedeutet, wenn man die Werte von darin verwendeten Konstanten sieht. Wenn ein numerisches Literal nur einmal für einen bestimmten Zweck verwendet wird, liefert das Schreiben des Literal an der Stelle, an der es verwendet wird, häufig verständlicheren Code als das Zuweisen einer Bezeichnung an einer Stelle und das Verwenden seines Werts an einer anderen Stelle.

Superkatze
quelle
7

Ich würde gerne nur die Nummer verwenden, aber ich denke, ein wichtiges Thema wurde noch 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 müssen jetzt zu ihr wechseln, um stattdessen GiB zu berechnen. Das Ändern der Konstante ist einfacher als ein globales Suchen / Ersetzen, bei dem Sie möglicherweise an einigen Stellen versehentlich die falsche oder an anderen Stellen die falsche Konstante ändern.

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 wenig konstruiertes Beispiel, aber in einigen Codebasen kann dies zu Problemen beim Umgestalten oder Aktualisieren für neue Anforderungen führen. In diesem 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 "richtiger" betrachten.

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

Nick P
quelle