bash
Kabuğun aritmetik değerlendirme yetenekleri için belirlenmiş sınırlar vardır . Kılavuzu kabuk aritmetik ancak bu yönüyle ilgili özlü durumları :
Değerlendirme, taşma kontrolü olmaksızın sabit genişlikli tamsayılarda yapılır, ancak 0'a bölünme bir hapsolur ve bir hata olarak işaretlenir. İşleçler ve öncelikleri, ilişkilendirilebilirlikleri ve değerleri C dilindekilerle aynıdır.
Bunun hangi sabit genişlikli tamsayıya başvurduğu gerçekten hangi veri türünün kullanıldığı (ve bunun neden bunun ötesinde olduğunun özellikleri) ile ilgilidir, ancak sınır değeri /usr/include/limits.h
bu şekilde ifade edilir :
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
Ve bunu öğrendikten sonra, bu durumun böyle olduğunu teyit edebilirsiniz:
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
Bu 64 bitlik bir tamsayıdır ve aritmetik değerlendirme bağlamında doğrudan kabukta tercüme edilir:
# 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
Yani 2 ila 63 ve 2 64 -1, sen ne kadar ileri olduğunu ULONG_MAX yola gösteren negatif tamsayılar olsun 1 . Ne zaman böyle bir şey ile bazı sıradışı davranışı elde edebilir 0'a sıfırlanır, hiçbir uyarı ve değerlendirme kısmını get olduğunu gelseler tarafından limit ve taşmaları, o değerlendirme ulaştığı sağ ilişkisel örneği için üs:
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
...
Kullanmak sh -c 'command'
hiçbir şeyi değiştirmez, bu yüzden bunun normal ve uyumlu çıktı olduğunu varsaymak zorundayım. Artık aritmetik aralık ve sınır ve ifade değerlendirmesi için kabuğunda ne anlama geldiğine dair temel ama somut bir anlayışa sahip olduğumu düşündüğüme göre, Linux'taki diğer yazılımların hangi veri türlerini kullandığına hızlıca bakabileceğimi düşündüm. bash
Bu komutun girdisini tamamlamak için bazı kaynaklar kullandım :
{ 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)
if
İfadeler ile daha fazla çıktı var ve ben awk
de vb gibi bir komut arayabilirsiniz . Kullandığım düzenli ifadenin bc
ve gibi keyfi hassas araçlar hakkında bir şey yakalamadığını fark ettim dc
.
Sorular
awk
Aritmetik değerlendirmeniz aşıldığında sizi uyarmamanın mantığı nedir ( 2 ^ 1024 değerini değerlendirirken olduğu gibi )? Bir şeyi değerlendirirken neden son 63'e 2 63 ve 2 64 -1 arasındaki negatif tamsayılar maruz kalıyor?- Bazı UNIX lezzet etkileşimli ULONG_MAX değişebilir bir yerde okudum? Bunu duyan var mı?
- Birisi işaretsiz tam sayı maksimum değerini keyfi olarak değiştirirse,
limits.h
yeniden derlersebash
, ne olmasını bekleyebiliriz?
Not
1. Çok basit ampirik şeyler olduğu için gördüklerimi daha net bir şekilde göstermek istedim. Ne fark ettim:
- (a) <2 ^ 63-1 veren herhangi bir değerlendirme doğrudur
- (b) => 2 ^ 63'e kadar 2 ^ 64 veren herhangi bir değerlendirme negatif bir tamsayı verir:
- Bu tamsayının aralığı x ila y'dir. x = -9223372036854775808 ve y = 0.
Bu göz önüne alındığında, (b) gibi bir değerlendirme 2 ^ 63-1 artı x..y içinde bir şey olarak ifade edilebilir. Örneğin, kelimenin tam anlamıyla (2 ^ 63-1) +100 002'yi değerlendirmemiz istendiğinde (ancak (a) 'dan daha küçük bir sayı olabilir) -9223372036854675807 alırız. Ben sadece sanırım bariz söylüyorum ama bu aynı zamanda aşağıdaki iki ifade anlamına gelir:
- (2 ^ 63-1) + 100002 VE;
- (2 ^ 63-1) + (LLONG_MAX - {kabuğun bize ne verdiğini ((2 ^ 63-1) + 100 002), ki bu -9223372036854675807}) sahip olduğumuz pozitif değerleri kullanarak;
- (2 ^ 63-1) + (9223372036854775807-9223372036854675807 = 100000)
- = 9223372036854775807 + 100000
gerçekten çok yakın. İkinci ifade (2 ^ 63-1) + 100 002 dışında yani "2" dir. Demek istediğim, 2 ^ 64'ten ne kadar uzakta olduğunuzu gösteren negatif tamsayılar olsun. Demek istediğim, bu negatif tamsayılar ve sınırların bilgisi ile, bash kabuğundaki x..y aralığında değerlendirmeyi bitiremezsiniz, ancak başka bir yerde yapabilirsiniz - veriler bu anlamda 2 ^ 64'e kadar kullanılabilir (ekleyebilirim kağıt üzerine koyun veya bc'de kullanın). Ancak bunun ötesinde, aşağıdaki Q'da açıklandığı gibi sınıra ulaşıldığından davranış 6 ^ 6 ^ 6 ile benzerdir ...
bc
, örneğin: kullanın $num=$(echo 6^6^6 | bc)
. Ne yazık ki, bc
satır sonları koyar, bu yüzden num=$(echo $num | sed 's/\\\s//g')
daha sonra yapmanız gerekir ; eğer bir boruda yaparsanız, sed ile garip olan gerçek yeni satır karakterleri vardır, ancak num=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
çalışır. Her iki durumda da, artık kullanılabilecek bir tamsayı vardır, örneğin num2=$(echo "$num * 2" | bc)
.
bc
, ayarlayarak bu satır sonu özelliğini devre dışı bırakabileceğinizi belirtti BC_LINE_LENGTH=0
.