Pernah mencoba menjumlahkan semua angka dari 1 hingga 2.000.000 dalam bahasa pemrograman favorit Anda? Hasilnya mudah dihitung secara manual: 2.000.001.000.000, yang sekitar 900 kali lebih besar dari nilai maksimum integer 32bit yang tidak ditandatangani.
C # print out -1453759936
- nilai negatif! Dan saya kira Java melakukan hal yang sama.
Itu berarti ada beberapa bahasa pemrograman umum yang mengabaikan Arithmetic Overflow secara default (dalam C #, ada opsi tersembunyi untuk mengubah itu). Itu perilaku yang terlihat sangat berisiko bagi saya, dan bukankah tabrakan Ariane 5 disebabkan oleh luapan seperti itu?
Jadi: apa keputusan desain di balik perilaku berbahaya seperti itu?
Sunting:
Jawaban pertama untuk pertanyaan ini mengungkapkan biaya pemeriksaan yang berlebihan. Mari kita jalankan program C # singkat untuk menguji asumsi ini:
Stopwatch watch = Stopwatch.StartNew();
checked
{
for (int i = 0; i < 200000; i++)
{
int sum = 0;
for (int j = 1; j < 50000; j++)
{
sum += j;
}
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
Di komputer saya, versi yang diperiksa membutuhkan 11015 ms, sedangkan versi yang tidak diperiksa mengambil 4125 ms. Yaitu langkah-langkah pemeriksaan memakan waktu hampir dua kali lipat dari menambahkan angka (total 3 kali waktu asli). Tetapi dengan 10.000.000.000 pengulangan, waktu yang dibutuhkan oleh cek masih kurang dari 1 nanosecond. Mungkin ada situasi di mana itu penting, tetapi untuk sebagian besar aplikasi, itu tidak masalah.
Edit 2:
Saya mengkompilasi ulang aplikasi server kami (layanan Windows yang menganalisis data yang diterima dari beberapa sensor, melibatkan sejumlah angka) dengan /p:CheckForOverflowUnderflow="false"
parameter (biasanya, saya mengaktifkan pemeriksaan overflow) dan menggunakannya pada perangkat. Pemantauan nagios menunjukkan bahwa rata-rata beban CPU tetap pada 17%.
Ini berarti bahwa hit kinerja yang ditemukan dalam contoh buatan di atas sama sekali tidak relevan untuk aplikasi kita.
(1..2_000_000).sum #=> 2000001000000
. Lain salah satu bahasa favorit saya: sum [1 .. 2000000] --=> 2000001000000
. Tidak favorit saya: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Agar adil, yang terakhir adalah curang.)
Integer
di Haskell adalah presisi sewenang-wenang, itu akan menampung nomor berapa pun selama Anda tidak kehabisan RAM yang dapat dialokasikan.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
itu indikasi loop sedang dioptimalkan. Kalimat itu juga bertentangan dengan angka-angka sebelumnya yang tampak sangat valid bagi saya.
checked { }
bagian untuk menandai bagian-bagian dari kode yang harus melakukan pemeriksaan Arithmetic Overflow. Ini karena kinerja