Ada batasan yang ditetapkan untuk kemampuan evaluasi aritmatika bash
kerang. 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 terjebak dan ditandai sebagai kesalahan. Operator
dan prioritas, asosiatif, dan nilainya sama seperti di
bahasa C.
Integer lebar tetap mana yang dimaksud yang benar-benar tentang tipe data which digunakan (dan secara spesifik mengapa ini melebihi ini) tetapi nilai batas dinyatakan dalam /usr/include/limits.h
dengan cara ini:
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
Dan setelah Anda mengetahuinya, Anda dapat mengkonfirmasi keadaan fakta ini seperti ini:
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
Ini adalah bilangan bulat 64 bit dan ini diterjemahkan langsung di 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 dan 2-1, Anda mendapatkan bilangan bulat negatif yang menunjukkan seberapa jauh Anda dari ULONG_MAX. Ketika evaluasi mencapai batas itu dan meluap, dengan urutan apa pun, Anda tidak mendapatkan peringatan dan bagian dari evaluasi itu disetel ulang ke 0 yang dapat menghasilkan beberapa perilaku yang tidak biasa dengan sesuatu seperti asosiasi-kanan eksponensial 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 tetapi konkret tentang rentang dan batas aritmatika dan apa artinya di shell untuk evaluasi ekspresi, saya pikir saya dapat dengan cepat mengintip tipe data apa yang digunakan perangkat lunak lain di Linux. Saya menggunakan beberapa bash
sumber Saya harus 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 perhatikan ekspresi reguler yang saya gunakan tidak menangkap apa pun tentang alat presisi arbitrer yang saya miliki seperti bc
dan dc
.
Pertanyaan
- Apa alasan untuk tidak memperingatkan Anda (seperti
awk
lakukan ketika mengevaluasi 2^1024) ketika evaluasi aritmatika Anda meluap? Mengapa bilangan bulat negatif antara 2 dan 2-1 diperlihatkan kepada pengguna akhir saat dia mengevaluasi sesuatu? - Saya pernah membaca bahwa beberapa rasa UNIX dapat mengubah ULONG_MAX secara interaktif? Ada yang pernah dengar ini?
- Jika seseorang secara sewenang-wenang mengubah nilai unsigned integer maximum di
limits.h
, lalu kompilasi ulangbash
, apa yang bisa kita harapkan akan terjadi?
Jawaban yang Diterima:
Jadi antara 2^63 dan 2^64-1, Anda mendapatkan bilangan bulat negatif yang menunjukkan seberapa jauh Anda dari ULONG_MAX.
Tidak. Bagaimana menurut Anda? Dengan contoh Anda sendiri, maksimumnya adalah:
> max=$((2**63 - 1)); echo $max
9223372036854775807
Jika "meluap" berarti "Anda mendapatkan bilangan bulat negatif yang menunjukkan seberapa jauh Anda dari ULONG_MAX", lalu jika kita menambahkan satu, bukankah kita akan mendapatkan -1? Tapi sebaliknya:
> echo $(($max + 1))
-9223372036854775808
Mungkin maksud Anda ini adalah nomor yang dapat Anda tambahkan ke $max
untuk mendapatkan selisih negatif, karena:
> echo $(($max + 1 + $max))
-1
Tapi ini sebenarnya tidak terus berlaku:
> echo $(($max + 2 + $max))
0
Ini karena sistem menggunakan komplemen dua untuk mengimplementasikan bilangan bulat bertanda. Nilai yang dihasilkan dari overflow BUKAN merupakan upaya untuk memberi Anda perbedaan, perbedaan negatif, dll. Ini secara harfiah merupakan hasil dari pemotongan nilai ke bit dalam jumlah terbatas, kemudian ditafsirkan sebagai bilangan bulat bertanda komplemen dua. Misalnya, alasan $(($max + 1 + $max))
keluar sebagai -1 karena nilai tertinggi dalam komplemen dua adalah semua bit yang ditetapkan kecuali bit tertinggi (yang menunjukkan negatif); menambahkan ini bersama-sama pada dasarnya berarti membawa semua bit ke kiri sehingga Anda mendapatkan (jika ukurannya 16-bit, dan bukan 64):
11111111 11111110
Bit (tanda) tinggi sekarang disetel karena terbawa dalam penambahan. Jika Anda menambahkan satu lagi (00000000 000000001) ke dalamnya, maka Anda telah semua bit disetel , yang dalam komplemen duanya adalah -1.
Saya pikir itu sebagian menjawab bagian kedua dari pertanyaan pertama Anda — “Mengapa bilangan bulat negatif… diekspos ke pengguna akhir?”. Pertama, karena itu adalah nilai yang benar menurut aturan bilangan komplemen dua 64-bit. Ini adalah praktik konvensional dari sebagian besar (lainnya) bahasa pemrograman tingkat tinggi tujuan umum (saya tidak bisa memikirkan yang tidak melakukan ini), jadi bash
adalah mengikuti konvensi. Yang juga merupakan jawaban untuk bagian pertama dari pertanyaan pertama — “Apa alasannya?”:ini adalah norma dalam spesifikasi bahasa pemrograman.
WRT pertanyaan ke-2, saya belum pernah mendengar tentang sistem yang secara interaktif mengubah ULONG_MAX.
Jika seseorang secara sewenang-wenang mengubah nilai unsigned integer maximum di limit.h, lalu mengkompilasi ulang bash, apa yang bisa kita harapkan akan terjadi?
Tidak ada bedanya dengan cara aritmatika keluar, karena ini bukan nilai sembarang yang digunakan untuk mengonfigurasi sistem — ini adalah nilai kenyamanan yang menyimpan konstanta abadi yang mencerminkan perangkat keras. Dengan analogi, Anda dapat mendefinisikan ulang c menjadi 55 mph, tetapi kecepatan cahaya masih akan 186.000 mil per detik. c bukan angka yang digunakan untuk mengonfigurasi alam semesta — ini adalah deduksi tentang sifat alam semesta.
Terkait:Python – Tidak ada file atau direktori seperti itu tetapi saya dapat melihatnya!?
ULONG_MAX persis sama. Itu disimpulkan/dihitung berdasarkan sifat bilangan N-bit. Mengubahnya di limits.h
akan menjadi ide yang sangat buruk jika konstanta itu digunakan di suatu tempat dengan asumsi itu seharusnya mewakili realitas sistem .
Dan Anda tidak dapat mengubah kenyataan yang dipaksakan oleh perangkat keras Anda.