すべてのマジックナンバーは同じですか?


77

最近のプロジェクトでは、バイトからキロバイト kibibyteに変換する必要がありました。コードは簡単でした:

var kBval = byteVal / 1024;

それを書いた後、私は機能の残りを動かして、先へ進みました。

しかし、後になって、コードにマジックナンバーを埋め込んだのではないかと思い始めました。私の一部は、数が固定された定数であり、容易に理解されるべきだから、それは大丈夫だと言います。しかし、私の別の部分は、のような定義済み定数にラップされている場合、それは非常に明確だったと思いBYTES_PER_KBYTEます。

それで、よく知られた定数である数字は本当に魔法のようなものですか?


関連する質問:

数字はいつ魔法の数字ですか?そして、コード内のすべての数字は「マジックナンバー」と見なされますか?-似ていますが、私が尋ねているものよりもはるかに広範な質問です。私の質問は、よく知られている定数に焦点を当てていますが、これらの質問では取り上げていません。

マジックナンバーの削除:「いいえ」と言うタイミングはいつですか?も関連していますが、定数がマジックナンバーであるかどうかではなく、リファクタリングに焦点を当てています。


17
私は実際に、次のような定数を作成したプロジェクトに取り組みましたFOUR_HUNDRED_FOUR = 404。私は、彼らが代わりにリテラルの定数文字列を使用する方法について好戦的だった別のプロジェクトに取り組んで、その彼らが持っていた数十人、ように見えたコードの行DATABASE = "database"
ロブ・

82
確かに使う1024そうでない場合は、あなたの開発チームは、それはそれは「キロバイト」または「kibibytes」で天気をについて議論時間ですすべてを過ごすことになりますので、。
スティーブンバーナップ14

6
1024はkibiであり#define KIBI、1024はMEBI1024 * 1024 であると考えるかもしれません…
ysdx 14

6
@Rob Y:古き良きFortranプログラマのようですね。そのプログラミング言語があるため強制的にそうするプログラマを。はい、そこに次のような定数が表示されますZERO=0, ONE=1, TWO=2と、プログラムは他の言語に移植されている(彼らの言語を切り替えるときやプログラマが動作を変更しないでください)あなたもそこに表示されます、あなたがする必要が祈る誰かがそれを変更しないことをONE=2...
ホルガー14

4
@NoctisSkytower私のチームは、使用する複数の言語と、それらの言語間で一貫性のない実装の可能性があるため、ビットシフト演算子の代わりに明示的な除算ステートメントを使用することを好みます。同様に、負の値はビット単位のシフトで一貫して処理されません。必ずしも負のバイト値を持っているとは限りませんが、変換する他の測定単位で負の値を持っていることは確かです。

回答:


103

すべてのマジックナンバーが同じというわけではありません。

その場合、その定数は問題ないと思います。マジックナンバーの問題は、それらがマジックである場合、つまり、その起源が何であるのか、値がなぜそれなのか、または値が正しいかどうかが不明な場合です。

BYTES_PER_KBYTEの後ろに1024を隠すことは、それが正しいかどうかすぐに表示されないことも意味します。

値が1024である理由を誰もがすぐに知ることを期待します。一方、バイトをメガバイトに変換する場合は、定数BYTES_PER_MBYTEなどを定義します。それが正しいことさえ。

同じことが、要件または標準によって決定される値についても言えます。これらの値は、1つの場所でのみ使用されます。他の場所で定義して両方の部分を追いかけるよりも簡単に対処できるように、関連するソースへのコメントを付けて定数を適切に配置するだけです。たとえば:

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

私はより良いと思う

SomeTest = DataSample < SOME_THRESHOLD_VALUE

SOME_THRESHOLD_VALUE私の意見では、が複数の場所で使用されている場合にのみ、定数を定義する価値があります。


67
「彼らは魔法のときマジックナンバーに問題がある」 -これは、そのようなそのコンセプトの華麗な解説!いまとても真剣なんだ!その文だけに+1。
ヨルグW

20
ここに私が思いついたものがあります:「問題は数字ではなく、魔法です。」
ヨルグWミットタグ14

10
1024は誰に明らかですか?それはすべての魔法の数字の正当化ではありませんか?すべてのマジックナンバーが使用されるのは、それらを書いた人には明らかだからです。9.8も明らかではありませんか?私にとってそれは地球上の重力の加速であることがかなり明らかですが、それにもかかわらず私は定数を作成します。
Tulainsコルドバ

15
いいえ。「より良い」例のようなコメントは、大きな赤い旗です。それは、その時点でそれを書いている人の読みやすさのテストさえパスしないコードです。例を挙げましょう。e^i*pi = -1は、よりもはるかに明示的(より良い)です2.718^i*3.142 = -1。変数は重要であり、一般的なコードのためだけのものではありません。コードは最初に読み取り、次にコンパイルするために記述されています。また、仕様が変更されます(多く)。1024はおそらく設定にあるべきではありませんが、3.5はそうあるべきです。
ネイサンクーパー14

51
1024 ^ 2の定数も使用しません。1024*1024plz!
軌道での軽量レース

44

マジックナンバーに関しては、2つの質問があります。

番号には名前がありますか?

名前を読むと、その背後にある数字の目的を理解できるので、名前は便利です。定数に名前を付けることが名前は、それが置き換わる数よりも理解しやすい場合は、読みやすさを向上し、定数名は簡潔です。

明らかに、piなどの定数。意味のある名前を付けてください。1024のような値BYTES_PER_KBでも構いませんが、開発者なら誰でも1024の意味を知っていると思います。ソースコードの対象読者は、2のさまざまな力とそれらが使用される理由を知っているバックグラウンドを持つプロのプログラマです。

複数の場所で使用されていますか?

名前は定数の長所の1つですが、もう1つは再利用性です。値が変更される可能性がある場合、複数の場所で値を探し出す必要はなく、1か所で変更できます。

あなたの質問

あなたの質問の場合、私はこの番号をそのまま使用します。

名前:その番号には名前がありますが、実際に役立つものではありません。要件ドキュメントで指定されている数学定数または値を表すものではありません。

場所:複数の場所で使用されても、変更されることはないため、この利点は無効になります。


1
マジックナンバーの代わりに定数を使用する理由は、前述の数字が変わるためだけではなく、読みやすさと自己文書化のためでもあります。
Tulainsコルドバ

4
@ user61852:名前付き定数は常に読みやすいとは限りません。彼らはしばしばそうですが、常にではありません。
whatsisname

2
個人的には、これらの2つの質問を代わりに使用します。「この値はプログラムの存続期間中に変化しますか?」「このソフトウェアの開発を期待している開発者は、この番号の目的を理解できるでしょうか?」
スティーブンバーナップ14

4
Y2Kの問題ですか?ここで関連があるかどうかはわかりません。ええ、 'date-1900'のようなコードはたくさんありましたが、そのコードでは、問題はマジックナンバー "1900"ではありませんでした。
スティーブンバーナップ14

1
この答えは、1024が間違いなく1である「明白な」数字は、誰かが名前付き定数を定義している場合でも、他の開発者が自発的に数字として書く可能性が高いという言及から恩恵を受けることができます。バイト量の変換で1024を使用する必要がある場合、1があることをまだ知らなかった場合、1024の既存の定数のソースコードを検索することさえ考えないでしょう。
ハイド14

27

この引用

問題となるのは数字ではなく、魔法です。

JörgW Mittagによると、この質問には非常によく答えています。

一部の数値は、特定のコンテキスト内で単に魔法ではありません。質問で提供されている例では、測定単位は変数名で指定されており、実行されている操作は非常に明確でした。

だから、1024文脈はそれが変換に使用する適切な、一定の値だ、それは非常に明確であることになるので、魔法ではありません。

同様に、例:

var numDays = numHours / 24; 

1日24時間あることがよく知られているため、同様に明確で魔法的ではありません。


21
しかし...しかし... 24は変わる可能性があります!地球は自転遅くしており、最終的には25時間になります!(もちろん、私たちはすべて、ソフトウェア、他の誰かの問題のメンテナンスを行うこと、それまでに死んでいるだろう)

14
ソフトウェアが火星に展開された後はどうなりますか?あなたは、注入すべきことであるが、一定の...
durron597

8
@ durron597:どのようなプログラムが遅いために地球のために十分な長さで実行されている場合その時。定数を注入するのではなく、タイムスタンプを受け入れ(現在はデフォルト)、タイムスタンプが下がる日の時間数を返す関数;
Steve Jessop 14

13
YaはYAGNIを学ぶ必要があります。
whatsisname

3
@ durron597火星に計時ソフトウェアを展開しても、特別なことは何も起こりません。慣例により火星の日は24時間ですが、1時間は地球よりも2.7%長いためです。もちろん、地球恒星日も地球太陽日も正確に24時間ではないため(正確な数字は同じページに記載されています)、24 とにかく使用できません!イズカタが言及したように、うるう秒は痛い。たぶん24、地球よりも火星の定数を実際に使用する方が幸運でしょう!
CVn 14

16

他のポスターは、起こっている転換は「明白」であると述べましたが、私は同意しません。この時点での元の質問には、次のものが含まれます。

キロバイトキビバイト

だから私はすでに著者が混乱していることを知っています。ウィキペディアのページは混乱を増しています:

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

したがって、「キロバイト」は1000と1024の両方の係数を意味するために使用できますが、略記の違いは「k」の大文字化のみです。さらに、1024はキロバイト(JEDEC)またはキビバイト(IEC)を意味します。混乱をすべて意味のある名前の定数で完全に粉砕してみませんか?ところで、このスレッドは "BYTES_PER_KBYTE"を頻繁に使用しており、それはそれほど曖昧ではありません。KBYTE:KIBIBYTEまたはKILOBYTEですか?私は、JEDECを無視して持っていることを好むだろうBYTES_PER_KILOBYTE = 1000BYTES_PER_KIBIBYTE = 1024。混乱はもうありません。

私や他の多くの人々がマジックナンバーの命名について「好戦的」(コメンターを引用する)意見を持っている理由は、あなたがやろうとしていることを文書化し、曖昧さを取り除くことです。そして、あなたは実際に多くの混乱につながるユニットを選びました。

表示された場合:

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

そうすれば、著者が何を意図していたかがすぐに明らかになり、あいまいさはありません。定数は数秒でチェックできます(別のファイルにある場合でも)。そのため、「インスタント」ではありませんが、瞬時に十分に近い値です。

最終的には、あなたがそれを書いているときそれは明白かもしれませんが、後でそれに戻ったときそれはそれほど明白ではないでしょう、そして誰かがそれを編集するときそれはさらに明白ではないかもしれません。定数を作成するには10秒かかります。ユニットの問題をデバッグするのに30分以上かかる可能性があります(コードが飛び出してユニットが間違っていることを伝えないので、それを理解するために自分で計算する必要があります。ユニットをチェックする前に10通りの道を探し出す可能性があります)。


2
良いカウンター答え。個々のチームカルチャーを考慮に入れると、より強力になります。もしあなたが私のSEプロファイルを信じていたら、私はそれらの特定の標準よりも前の年齢です。したがって、唯一の混乱は「現在の(非)標準用語は何ですか?」そして、同じ用語(非)難しさを持っている仲間の恐竜のチームと一緒に仕事をしていると仮定しても、おそらく安全でしょう。

@ GlenH7:私見、2のべき乗ベースのユニットは、2のべき乗のサイズのチャンクで割り当てられているため、ストレージ用に保持する必要がありました。最小割り当てサイズが4096バイトである場合、256個の最小サイズのファイルを保持するのに必要なストレージの量、または244.140625のそのようなファイルを保持するのに必要なストレージの量の単位を持っている方が理にかなっていますか?個人的には、ハードドライブメーカーのメガバイトと他のメガバイトの違いは、テレビの対角線のインチと実際の対角線のインチの違いに似ていると考えています。
supercat 14

@Ryan:この特定のケースでは、むしろ標準ユニットの採用について好戦的です-KBは1000バイトまたはコードが間違っており、1024バイトはKiBまたはコードが間違っています。これが、「ユニットがあいまいな」問題を克服する唯一の方法です。「魔法の定数」(などKB)を定義する人が異なれば、助けにはなりません。
ブレンダン14

11

数値を参照するように名前を定義することは、その名前を使用する1つの場所で異なる値が必要なときはいつでも、その値が必要になる可能性が高いことを示唆しています。また、名前に割り当てられた数値を変更することは、値を変更する正当な方法であることを示唆する傾向があります。このような意味合いは、真である場合に役立ち、偽である場合は危険です。

2つの異なる場所が特定のリテラル値(1024など)を使用するという事実は、プログラマーに1つを変更するように促す変更が、プログラマーに他の人を変更したいという気にさせる可能性があることを弱く示唆していますが、その意味は適用するよりもはるかに弱いですプログラマがそのような定数に名前を割り当てた場合。

そのようなものの主な危険性#define BYTES_PER_KBYTE 1024は、遭遇printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE));する人に、コードに数千バイトを使用させる安全な方法は#defineステートメントを変更することであることを示唆する可能性があることです。ただし、たとえば、他の無関係なコードがオブジェクトのサイズをキロバイト単位で受け取り、その定数を使用してバッファを割り当てる場合、このような変更は悲惨なものになる可能性があります。

#define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024and を使用することは合理的かもしれませんが#define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024、定数1024によって提供される目的ごとに異なる名前を割り当てますが、その結果、多くの識別子が一度だけ定義されて使用されることになります。さらに、多くの場合、使用されているコードを見ると値の意味を理解するのが最も簡単であり、使用されている定数の値を見るとコードの意味を理解するのが最も簡単です。数値リテラルが特定の目的のために1回だけ使用される場合、使用される場所でリテラルを記述すると、多くの場合、ある場所でラベルを割り当ててその値を別の場所で使用するよりも理解しやすいコードが生成されます。


7

私は数字だけを使用することに傾倒しますが、重要な問題が1つも提起されていないと思います。同じ数字は異なるコンテキストで異なることを意味する可能性があり、リファクタリングが複雑になる可能性があります。

1024は、MiBあたりのKiBの数でもあります。1024を使用して、その計算をどこか、または複数の場所でも表すと仮定し、GiBを計算するように変更する必要があるとします。定数を変更することは、ある場所で誤って間違ったものを変更したり、他の場所で見落としたりする可能性があるグローバルな検索/置換よりも簡単です。

あるいは、怠updatedなプログラマーによって導入されたビットマスクであり、いつか更新する必要がある場合もあります。

これは少し不自然な例ですが、一部のコードベースでは、新しい要件に合わせてリファクタリングまたは更新するときに問題が発生する可能性があります。この特定のケースでは、特に再利用のためにメソッドに計算を含めることができる場合、プレーンな数字が本当に悪い形であるとは思わないでしょう。

しかし、名前付き定数を使用する場合は、supercatが言うように、コンテキストも重要であるかどうか、および複数の名前が必要かどうかを考慮することが重要です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.