Apakah semua angka ajaib dibuat sama?

77

Pada proyek baru-baru ini, saya perlu mengkonversi dari byte ke kilobyte kibibyte . Kode itu cukup mudah:

var kBval = byteVal / 1024;

Setelah menulis itu, saya mendapatkan sisa fungsi bekerja & pindah.

Tetapi kemudian, saya mulai bertanya-tanya apakah saya baru saja memasukkan angka ajaib dalam kode saya. Sebagian dari diriku mengatakan itu baik-baik saja karena jumlahnya adalah konstanta yang tetap dan harus mudah dipahami. Tetapi bagian lain dari diriku berpikir itu akan sangat jelas jika dibungkus dengan konstanta yang didefinisikan seperti BYTES_PER_KBYTE.

Jadi, apakah bilangan yang dikenal sebagai konstanta benar-benar ajaib atau tidak?


Pertanyaan-pertanyaan Terkait:

Kapan angka merupakan angka ajaib? dan Apakah setiap angka dalam kode dianggap sebagai "angka ajaib"? - mirip, tetapi pertanyaan yang jauh lebih luas dari yang saya tanyakan. Pertanyaan saya terfokus pada angka konstan terkenal yang tidak dibahas dalam pertanyaan itu.

Menghilangkan Angka Ajaib: Kapan waktunya untuk mengatakan "Tidak"? juga terkait, tetapi difokuskan pada refactoring sebagai kebalikan dari apakah angka konstan adalah angka ajaib atau tidak.

Komunitas
sumber
17
Saya benar-benar bekerja pada sebuah proyek di mana mereka telah menciptakan konstanta seperti FOUR_HUNDRED_FOUR = 404,. Saya bekerja pada proyek lain di mana mereka militan tentang menggunakan string konstan daripada literal, sehingga mereka memiliki lusinan baris dalam kode yang tampak seperti,DATABASE = "database"
Rob
82
Pasti digunakan 1024, karena kalau tidak tim dev Anda akan menghabiskan semua waktu berdebat tentang apakah itu "kilobytes" atau "kibibytes".
Gort the Robot
6
Anda mungkin menganggap bahwa 1024 adalah kibi dan #define KIBI1024, MEBIseperti 1024 * 1024 ...
ysdx
6
@Rob Y: terdengar seperti programmer Fortran tua yang baik. Karena bahasa pemrograman itu memaksa programmer untuk melakukannya. Ya, di sana Anda akan melihat konstanta seperti ZERO=0, ONE=1, TWO=2dan ketika program porting ke bahasa lain (atau programer tidak mengubah perilaku ketika mengganti bahasa mereka) Anda akan melihatnya di sana juga dan Anda harus berdoa agar tidak ada orang yang mengubahnya ke ONE=2...
Holger
4
@NoctisSkytower Tim saya lebih suka menggunakan pernyataan pembagian eksplisit daripada operator yang menggeser bit karena beberapa bahasa yang kami gunakan dan implementasi yang berpotensi tidak konsisten di semua bahasa itu. Demikian juga, nilai-nilai negatif ditangani secara tidak konsisten dengan pergeseran bitwise. Meskipun kita mungkin tidak memiliki nilai byte negatif, kita tentu memiliki nilai negatif dengan satuan ukuran lain yang kita konversi.

Jawaban:

103

Tidak semua angka ajaib itu sama.

Saya pikir dalam contoh itu, konstanta itu OK. Masalah dengan angka ajaib adalah ketika angka itu ajaib, yaitu tidak jelas asal usulnya, mengapa nilainya seperti apa adanya, atau apakah nilainya benar atau tidak.

Menyembunyikan 1024 di belakang BYTES_PER_KBYTE juga berarti Anda tidak melihat langsung apakah itu benar atau tidak.

Saya akan mengharapkan siapa pun untuk segera mengetahui mengapa nilainya 1024. Di sisi lain, jika Anda mengubah byte ke megabyte, saya akan mendefinisikan BYTES_PER_MBYTE konstan atau serupa karena konstanta 1.048.576 tidak begitu jelas sehingga 1024 ^ 2, atau bahkan itu benar.

Hal yang sama berlaku untuk nilai-nilai yang ditentukan oleh persyaratan atau standar, yang hanya digunakan di satu tempat. Saya menemukan hanya menempatkan konstanta di tempat dengan komentar ke sumber yang relevan agar lebih mudah ditangani daripada mendefinisikannya di tempat lain dan harus mengejar kedua bagian, misalnya:

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

Saya menemukan lebih baik daripada

SomeTest = DataSample < SOME_THRESHOLD_VALUE

Hanya ketika SOME_THRESHOLD_VALUEdigunakan di banyak tempat, tradeoff menjadi layak untuk mendefinisikan konstanta, menurut saya.

Apa namanya
sumber
67
"Masalah dengan angka ajaib adalah ketika mereka ajaib" - ini adalah seperti penjelasan brilian konsep itu! Saya serius! +1 untuk kalimat itu saja.
Jörg W Mittag
20
Inilah yang baru saja saya kemukakan: "itu bukan angka yang menjadi masalah, itu keajaiban."
Jörg W Mittag
10
1024 jelas bagi siapa? Bukankah itu pembenaran untuk setiap angka ajaib? Semua angka ajaib digunakan karena jelas bagi siapa pun yang menulisnya. Bukankah 9,8 juga jelas? Bagi saya itu cukup jelas itu adalah percepatan gravitasi di bumi, tetapi saya akan tetap menciptakan konstanta, karena apa yang jelas bagi saya mungkin tidak jelas bagi orang lain.
Tulains Córdova
15
Tidak. Sebuah komentar seperti yang ada pada contoh "yang lebih baik" adalah bendera merah besar. Itu kode yang bahkan tidak lulus tes keterbacaan dari orang yang menulisnya saat itu. Saya akan memberi contoh. e^i*pi = -1jauh lebih eksplisit (lebih baik) daripada 2.718^i*3.142 = -1. Variabel penting dan mereka bukan hanya untuk kode umum. Kode ditulis untuk membaca pertama, kompilasi kedua. Juga, spesifikasi berubah (banyak). Sementara 1024 mungkin tidak harus dalam konfigurasi 3,5 suara seperti seharusnya.
Nathan Cooper
51
Saya juga tidak akan menggunakan konstanta untuk 1024 ^ 2; 1024*1024tolong!
Lightness Races in Orbit
44

Ada dua pertanyaan yang saya tanyakan dalam hal angka ajaib.

Apakah nomor itu memiliki nama?

Nama berguna karena kita dapat membaca nama dan memahami tujuan nomor di belakangnya. Konstanta penamaan dapat meningkatkan keterbacaan jika nama lebih mudah dipahami daripada nomor yang diganti dan nama konstan adalah singkat.

Jelas, konstanta seperti pi, e, et al. memiliki nama yang bermakna. Nilai seperti 1024 bisa BYTES_PER_KBtetapi saya juga berharap bahwa setiap pengembang akan tahu apa artinya 1024. Audiens yang dituju untuk kode sumber adalah pemrogram profesional yang harus memiliki latar belakang untuk mengetahui berbagai kekuatan dua dan mengapa mereka digunakan.

Apakah ini digunakan di banyak lokasi?

Sementara nama adalah salah satu kekuatan konstanta, yang lain adalah usabilitas. Jika suatu nilai kemungkinan berubah, itu dapat diubah di satu tempat alih-alih perlu memburunya di beberapa lokasi.

Pertanyaanmu

Dalam hal pertanyaan Anda, saya akan menggunakan nomor apa adanya.

Nama: ada nama untuk nomor itu, tetapi tidak ada yang benar-benar berguna. Ini tidak mewakili konstanta matematika atau nilai yang ditentukan dalam dokumen persyaratan apa pun.

Lokasi: bahkan jika digunakan di banyak lokasi, itu tidak akan pernah berubah, meniadakan manfaat ini.


sumber
1
Alasan menggunakan konstanta bukan angka ajaib bukan hanya karena angka tersebut akan berubah, tetapi juga untuk keterbacaan dan dokumentasi diri.
Tulains Córdova
4
@ user61852: konstanta bernama tidak selalu lebih mudah dibaca. Mereka sering, tetapi tidak selalu.
whatsisname
2
Secara pribadi, saya menggunakan dua pertanyaan ini sebagai gantinya: "Apakah nilai ini akan berubah sepanjang masa program?" dan "Apakah para pengembang yang saya harapkan dapat mengerjakan perangkat lunak ini memahami untuk apa angka ini?"
Gort the Robot
4
Maksud Anda masalah Y2K? Saya tidak yakin ini relevan di sini. Ya, ada banyak kode seperti 'tanggal - 1900', tetapi dalam kode itu, masalahnya bukan angka ajaib "1900".
Gort the Robot
1
Jawaban ini dapat mengambil manfaat dari penyebutan, bahwa beberapa angka "jelas", 1024 jelas merupakan satu, adalah sedemikian sehingga pengembang lain sangat mungkin untuk secara spontan menuliskannya sebagai angka, bahkan ketika seseorang menetapkan konstanta bernama untuk mereka. Saya untuk satu kemungkinan besar bahkan tidak akan berpikir untuk mencari kode sumber untuk konstan yang ada untuk 1024 jika saya belum tahu ada satu, jika saya perlu menggunakan 1024 dalam jumlah byte konversi.
hyde
27

Kutipan ini

Bukan angka itulah masalahnya, itu keajaiban.

seperti yang dikatakan oleh Jörg W Mittag menjawab pertanyaan ini dengan cukup baik.

Beberapa angka memang tidak ajaib dalam konteks tertentu. Dalam contoh yang diberikan dalam pertanyaan, satuan ukuran ditentukan oleh nama variabel dan operasi yang terjadi cukup jelas.

Jadi 1024tidak ajaib karena konteksnya membuatnya sangat jelas bahwa itu adalah nilai yang tepat dan konstan untuk digunakan untuk konversi.

Demikian juga contoh:

var numDays = numHours / 24; 

sama jelas dan tidak ajaib karena diketahui bahwa ada 24 jam dalam sehari.

Komunitas
sumber
21
Tapi ... tapi ... 24 bisa berubah! Bumi memperlambat rotasi dan akhirnya akan memiliki 25 jam! (Tentu saja kita semua akan mati pada saat itu, membuat pemeliharaan perangkat lunak itu menjadi masalah orang lain)
14
Apa yang terjadi setelah perangkat lunak Anda digunakan di Mars? Anda harus menyuntikkan konstanta itu ...
durron597
8
@ durron597: bagaimana jika program Anda berjalan cukup lama agar bumi melambat pada saat itu . Anda tidak boleh menyuntikkan konstanta, melainkan fungsi yang menerima cap waktu (default sekarang) dan mengembalikan jumlah jam pada hari ketika cap waktu jatuh ;-)
Steve Jessop
13
Ya, Anda perlu belajar YAGNI.
whatsisname
3
@ durron597 Tidak ada yang istimewa terjadi ketika perangkat lunak ketepatan waktu Anda digunakan di Mars, karena menurut konvensi Mars hari adalah 24 jam, tetapi setiap jam 2,7% lebih lama daripada di Bumi . Tentu saja, baik hari sidereal Bumi maupun hari matahari Bumi tidak tepat 24 jam (angka pastinya ada di halaman yang sama), jadi Anda 24 tetap tidak bisa menggunakannya ! Seperti yang disebutkan Izkata, detik kabisat terasa sakit. Mungkin Anda akan lebih beruntung menggunakan konstanta 24di Mars daripada di Bumi!
CVn
16

Poster-poster lain menyebutkan bahwa konversi terjadi 'jelas', tetapi saya tidak setuju. Pertanyaan awal, pada saat ini, termasuk:

kilobytes kibibytes

Jadi sudah saya tahu penulisnya sedang atau bingung. Halaman Wikipedia menambah kebingungan:

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

Jadi "Kilobyte" dapat digunakan untuk berarti faktor 1000 dan 1024, dengan satu-satunya perbedaan dalam steno adalah kapitalisasi huruf 'k'. Selain itu, 1024 dapat berarti kilobyte (JEDEC) atau kibibyte (IEC). Mengapa tidak menghancurkan semua kebingungan itu dengan konstanta dengan nama yang bermakna? BTW, utas ini telah sering menggunakan "BYTES_PER_KBYTE", dan itu tidak kalah mendua. KBYTE: apakah itu KIBIBYTE atau KILOBYTE? Saya lebih suka mengabaikan JEDEC dan memiliki BYTES_PER_KILOBYTE = 1000dan BYTES_PER_KIBIBYTE = 1024. Tidak ada lagi kebingungan.

Alasan mengapa orang-orang seperti saya, dan banyak orang lain di luar sana, memiliki pendapat 'militan' (mengutip komentar di sini) tentang penamaan angka-angka ajaib adalah tentang mendokumentasikan apa yang ingin Anda lakukan, dan menghilangkan ambiguitas. Dan Anda benar-benar memilih unit yang telah menyebabkan banyak kebingungan.

Jika saya melihat:

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

Maka segera jelas apa yang dimaksudkan penulis lakukan, dan tidak ada ambiguitas. Saya dapat memeriksa konstanta dalam hitungan detik (bahkan jika itu ada di file lain), jadi meskipun itu bukan 'instan', itu cukup dekat untuk instan.

Pada akhirnya, mungkin terlihat jelas ketika Anda menulisnya, tetapi akan menjadi kurang jelas ketika Anda kembali lagi nanti, dan mungkin bahkan lebih tidak jelas ketika orang lain mengeditnya. Dibutuhkan 10 detik untuk membuat konstanta; mungkin butuh setengah jam atau lebih untuk men-debug masalah dengan unit (kode tidak akan melompat keluar pada Anda dan memberi tahu Anda unit salah, Anda harus melakukan matematika sendiri untuk mengetahuinya, dan Anda mungkin akan memburu 10 jalan berbeda sebelum Anda memeriksa unit).

Shaz
sumber
2
Jawaban balik yang bagus. Akan lebih kuat jika Anda memperhitungkan budaya tim individu. Jika Anda mempercayai profil SE saya , saya sudah cukup tua untuk mendahului standar-standar khusus itu. Jadi satu-satunya kebingungan berasal dari "apa istilah standar saat ini (non-)?" Dan Anda mungkin akan aman dengan asumsi saya bekerja dengan tim sesama dinosaurus yang semuanya memiliki kesulitan terminologi (non-) yang sama.
@ GlenH7: IMHO, unit berbasis kekuatan seharusnya disimpan untuk penyimpanan, karena itu dialokasikan dalam potongan berukuran kekuatan dua. Jika ukuran alokasi minimum adalah 4096 byte, apakah lebih masuk akal untuk memiliki unit untuk jumlah penyimpanan yang diperlukan untuk menyimpan 256 file ukuran minimal, atau jumlah penyimpanan yang diperlukan untuk menyimpan 244.140625 file seperti itu? Secara pribadi, saya melihat perbedaan antara megabyte hard-drive dan megabyte lainnya untuk dianalogikan dengan perbedaan antara televisi yang mengatur inci diagonal dan inci diagonal nyata.
supercat
@Ryan: Untuk kasus khusus ini, saya lebih suka menjadi militan tentang adopsi unit standar - KB adalah 1000 byte atau kode salah, dan 1024 byte KiB atau kode salah. Ini adalah satu-satunya cara kita akan melewati masalah "unit yang ambigu". Orang yang berbeda mendefinisikan "konstanta sihir" (seperti KB) secara berbeda tidak akan membantu.
Brendan
11

Mendefinisikan nama sebagai merujuk pada nilai numerik menunjukkan bahwa setiap kali nilai yang berbeda diperlukan di satu tempat yang menggunakan nama itu, kemungkinan akan dibutuhkan di semua. Ini juga cenderung menyarankan bahwa mengubah nilai numerik yang diberikan pada nama adalah cara yang sah untuk mengubah nilai. Implikasi seperti itu bisa berguna ketika itu benar, dan berbahaya ketika itu salah.

Fakta bahwa dua tempat yang berbeda menggunakan nilai literal tertentu (misalnya 1024) akan lemah menunjukkan bahwa perubahan yang akan mendorong seorang programmer untuk mengubah satu agak mungkin menginspirasi programmer untuk ingin mengubah yang lain, tetapi implikasinya jauh lebih lemah daripada yang akan berlaku jika programmer menetapkan nama ke konstanta seperti itu.

Bahaya besar dengan sesuatu seperti itu #define BYTES_PER_KBYTE 1024adalah bahwa hal itu mungkin menyarankan kepada seseorang yang menemukan printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE));bahwa cara aman untuk menggunakan kode ribuan byte adalah dengan mengubah #definepernyataan. Perubahan seperti itu bisa menjadi bencana, namun, jika misalnya beberapa kode lain yang tidak terkait menerima ukuran objek dalam Kbytes dan menggunakan konstanta itu ketika mengalokasikan buffer untuknya.

Mungkin masuk akal untuk menggunakan #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024dan #define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024, menugaskan nama yang berbeda untuk setiap tujuan yang berbeda dilayani oleh konstanta 1024, tetapi itu akan menghasilkan banyak pengidentifikasi yang didefinisikan dan digunakan tepat sekali. Lebih jauh, dalam banyak kasus, lebih mudah untuk memahami apa arti suatu nilai jika seseorang melihat kode di mana ia digunakan, dan lebih mudah untuk mencari tahu di mana arti kode jika seseorang melihat nilai konstanta yang digunakan di dalamnya. Jika literal numerik hanya digunakan satu kali untuk tujuan tertentu, menulis literal di tempat di mana ia digunakan akan sering menghasilkan kode yang lebih mudah dipahami daripada menetapkan label untuk itu di satu tempat dan menggunakan nilainya di tempat lain.

supercat
sumber
7

Saya akan cenderung menggunakan hanya nomor, namun saya pikir satu masalah penting belum diangkat: Jumlah yang sama dapat berarti hal-hal yang berbeda dalam konteks yang berbeda, dan ini dapat mempersulit refactoring.

1024 juga merupakan jumlah KiB per MiB. Misalkan kita menggunakan 1024 untuk juga mewakili perhitungan itu di suatu tempat, atau di banyak tempat, dan sekarang kita perlu mengubahnya untuk menghitung GiB sebagai gantinya. Mengubah konstanta lebih mudah daripada pencarian / penggantian global tempat Anda dapat secara tidak sengaja mengubah yang salah di beberapa tempat, atau melewatkannya di tempat lain.

Atau bahkan bisa berupa topeng bit yang diperkenalkan oleh programmer malas yang perlu diperbarui suatu hari.

Ini sedikit contoh yang dibuat-buat tetapi dalam beberapa basis kode ini dapat menyebabkan masalah ketika refactoring atau memperbarui untuk persyaratan baru. Untuk kasus khusus ini, saya tidak akan menganggap angka polos sebagai bentuk yang benar-benar buruk terutama jika Anda dapat menyertakan perhitungan dalam metode untuk digunakan kembali, saya mungkin akan melakukannya sendiri tetapi menganggap konstanta lebih 'benar'.

Jika Anda menggunakan konstanta bernama, seperti dikatakan supercat, penting untuk mempertimbangkan apakah konteks juga penting, dan jika Anda memerlukan banyak nama.

Nick P
sumber