Ada batas yang ditetapkan untuk kemampuan evaluasi aritmatika dari bash
shell. Manual ini ringkas tentang aspek aritmatika shell ini tetapi menyatakan :
Evaluasi dilakukan dalam bilangan bulat dengan lebar tetap tanpa pemeriksaan overflow, meskipun pembagian dengan 0 terperangkap dan ditandai sebagai kesalahan. Operator dan prioritas, asosiatif, dan nilai-nilai mereka sama seperti dalam bahasa C.
Bilangan bulat dengan lebar tetap yang merujuk ini benar-benar tentang tipe data mana yang digunakan (dan spesifik mengapa hal ini berada di luar ini) tetapi nilai batas dinyatakan dengan /usr/include/limits.h
cara ini:
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
Dan begitu Anda tahu itu, Anda dapat mengkonfirmasi keadaan fakta ini seperti:
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
Ini adalah integer 64 bit dan ini diterjemahkan secara langsung dalam shell dalam konteks evaluasi aritmatika:
# echo $(((2**63)-1)); echo $((2**63)); echo $(((2**63)+1)); echo $((2**64))
9223372036854775807 //the practical usable limit for your everyday use
-9223372036854775808 //you're that much "away" from 2^64
-9223372036854775807
0
# echo $((9223372036854775808+9223372036854775807))
-1
Jadi antara 2 63 dan 2 64 -1, Anda mendapatkan bilangan bulat negatif yang menunjukkan seberapa jauh Anda dari ULONG_MAX Anda 1 . Ketika evaluasi mencapai batas itu dan meluap, dengan urutan apa pun itu, Anda tidak mendapat peringatan dan bagian dari evaluasi diatur ulang ke 0 yang dapat menghasilkan beberapa perilaku tidak biasa dengan sesuatu seperti eksponensial asosiatif-kanan misalnya:
echo $((6**6**6)) 0 // 6^46656 overflows to 0
echo $((6**6**6**6)) 1 // 6^(6^46656) = 6^0 = 1
echo $((6**6**6**6**6)) 6 // 6^(6(6^46656)) = 6^(6^0) = 6^1
echo $((6**6**6**6**6**6)) 46656 // 6^(6^(6^(6^46656))) = 6^6
echo $((6**6**6**6**6**6**6)) 0 // = 6^6^6^1 = 0
...
Menggunakan sh -c 'command'
tidak mengubah apa pun jadi saya harus menganggap ini adalah output yang normal dan sesuai. Sekarang saya pikir saya memiliki pemahaman dasar tapi konkret tentang rentang dan batas aritmatika dan apa artinya di shell untuk evaluasi ekspresi, saya pikir saya bisa dengan cepat mengintip pada tipe data apa yang digunakan perangkat lunak lain dalam Linux. Saya menggunakan beberapa bash
sumber yang saya miliki untuk melengkapi input dari perintah ini:
{ shopt -s globstar; for i in /path/to/source_bash-4.2/include/**/*.h /usr/include/**/*.h; do grep -HE '\b(([UL])|(UL)|())LONG|\bFLOAT|\bDOUBLE|\bINT' $i; done; } | grep -iE 'bash.*max'
bash-4.2/include/typemax.h:# define LLONG_MAX TYPE_MAXIMUM(long long int)
bash-4.2/include/typemax.h:# define ULLONG_MAX TYPE_MAXIMUM(unsigned long long int)
bash-4.2/include/typemax.h:# define INT_MAX TYPE_MAXIMUM(int)
Ada lebih banyak output dengan if
pernyataan dan saya dapat mencari perintah seperti awk
juga dll. Saya melihat ekspresi reguler yang saya gunakan tidak menangkap apa pun tentang alat presisi sewenang-wenang yang saya miliki seperti bc
dan dc
.
Pertanyaan
- Apa alasan untuk tidak memperingatkan Anda (seperti
awk
halnya ketika mengevaluasi 2 ^ 1024) ketika evaluasi aritmatika Anda meluap? Mengapa bilangan bulat negatif antara 2 63 dan 2 64 -1 terkena pengguna akhir ketika dia mengevaluasi sesuatu? - Saya telah membaca bahwa rasa UNIX dapat mengubah ULONG_MAX secara interaktif? Adakah yang pernah mendengar ini?
- Jika seseorang secara sewenang-wenang mengubah nilai maksimum integer unsigned
limits.h
, lalu mengkompilasi ulangbash
, apa yang dapat kita harapkan akan terjadi?
Catatan
1. Saya ingin mengilustrasikan lebih jelas apa yang saya lihat, karena itu adalah hal-hal empiris yang sangat sederhana. Yang saya perhatikan adalah:
- (a) Setiap evaluasi yang memberikan <2 ^ 63-1 adalah benar
- (B) Setiap evaluasi yang memberi => 2 ^ 63 hingga 2 ^ 64 memberikan bilangan bulat negatif:
- Kisaran bilangan bulat itu adalah x hingga y. x = -9223372036854775808 dan y = 0.
Mempertimbangkan hal ini, evaluasi yang seperti (b) dapat dinyatakan sebagai 2 ^ 63-1 ditambah sesuatu dalam x..y. Sebagai contoh jika kita benar-benar diminta untuk mengevaluasi (2 ^ 63-1) +100 002 (tetapi bisa lebih kecil daripada di (a)) kita mendapatkan -9223372036854675807. Saya hanya menyatakan yang jelas saya kira tetapi ini juga berarti bahwa dua ekspresi berikut:
- (2 ^ 63-1) + 100 002 DAN;
- (2 ^ 63-1) + (LLONG_MAX - {apa yang diberikan shell kepada kami ((2 ^ 63-1) + 100 002), yaitu -9223372036854675807}) dengan baik, menggunakan nilai positif yang kami miliki;
- (2 ^ 63-1) + (9223372036854775807 - 9223372036854675807 = 100 000)
- = 9223372036854775807 + 100.000
memang sangat dekat. Ekspresi kedua adalah "2" terpisah dari (2 ^ 63-1) + 100 002 yaitu apa yang kami evaluasi. Inilah yang saya maksudkan dengan Anda mendapatkan bilangan bulat negatif yang menunjukkan seberapa jauh Anda dari 2 ^ 64. Maksud saya dengan bilangan bulat negatif dan pengetahuan tentang batas, yah Anda tidak bisa menyelesaikan evaluasi dalam kisaran x..y di bash shell tetapi Anda bisa di tempat lain - data dapat digunakan hingga 2 ^ 64 dalam hal itu (saya bisa menambahkan itu di atas kertas atau menggunakannya dalam bc). Di luar itu, bagaimanapun, perilaku ini mirip dengan 6 ^ 6 ^ 6 karena batas tercapai seperti yang dijelaskan di bawah ini dalam Q ...
bc
, misalnya: $num=$(echo 6^6^6 | bc)
. Sayangnya, bc
menempatkan jeda baris, jadi Anda harus num=$(echo $num | sed 's/\\\s//g')
sesudahnya; jika Anda melakukannya di dalam pipa, ada karakter baris baru yang sebenarnya, yang canggung dengan sed, meskipun num=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
berfungsi. Dalam kedua kasus Anda sekarang memiliki bilangan bulat yang dapat digunakan, misalnya num2=$(echo "$num * 2" | bc)
,.
bc
dengan mengatur BC_LINE_LENGTH=0
.