Bitsel kaydırma ve Bash'deki en büyük tam sayı


16

Bu bir keşif sorusu, yani bu sorunun ne hakkında olduğundan tam olarak emin değilim, ama bash'in en büyük tamsayısı hakkında olduğunu düşünüyorum. Her neyse, bunu açıkça tanımlayacağım.

$ echo $((1<<8))
256

Biraz kaydırarak bir tamsayı üretiyorum. Ne kadar ileri gidebilirim?

$ echo $((1<<80000))
1

Görünüşe göre bu kadar değil. (1 beklenmedik ve buna geri döneceğim.) Ama,

$ echo $((1<<1022))
4611686018427387904

hala olumlu. Ancak bu değil:

$ echo $((1<<1023))
-9223372036854775808

Ve bir adım daha uzağa,

$ echo $((1<<1024))
1

Neden 1? Ve neden aşağıdakiler?

$ echo $((1<<1025))
2
$ echo $((1<<1026))
4

Birisi bu diziyi analiz etmek ister mi?

GÜNCELLEME

Makinem:

$ uname -a
Linux tomas-Latitude-E4200 4.4.0-47-generic #68-Ubuntu SMP Wed Oct 26 19:39:52 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

-9223372036854775808 = 0xF333333333333334. Bu komik görünen bir durum. Tabii ki, 4611686018427387904 = 0x4000000000000000. Kaydırılacak bit sayısı üzerinde bir tür sargı attığınızdan şüpheleniyorum. Bunu neden yapıyorsun?
CVn

6
@ MichaelKjörling Eğlence için

2
@ MichaelKjörling Hayır, değil. -9223372036854775808 0x8000000000000000 olur. Kontrol ederken son basamağı dışarıda bıraktınız: -922337203685477580 0xF33333333333333334 olur.
hvd

Yanıtlar:


27

Bash , intmax_taritmetik için değişkenler kullanır . Sisteminizde bunlar 64 bit uzunluğundadır, bu nedenle:

$ echo $((1<<62))
4611686018427387904

hangisi

100000000000000000000000000000000000000000000000000000000000000

ikili (1 ve ardından 62 0s). Tekrar kaydır:

$ echo $((1<<63))
-9223372036854775808

hangisi

1000000000000000000000000000000000000000000000000000000000000000

ikili (63 0s), ikisinin tamamlayıcı aritmetiğinde.

Temsil edilebilir en büyük tamsayıyı elde etmek için 1 çıkarmanız gerekir:

$ echo $(((1<<63)-1))
9223372036854775807

hangisi

111111111111111111111111111111111111111111111111111111111111111

ikili.

İçinde sivri dışarı olarak ilkkachu 'ın cevabı , değişen 64 bit üzerinde modülo 64 ofset alır x86 (kullanarak ister CPU RCLya SHLBehavior ediyoruz görme açıklıyor):

$ echo $((1<<64))
1

eşittir $((1<<0)). Böylece $((1<<1025))ise $((1<<1)), $((1<<1026))olduğu $((1<<2))...

Tür tanımlarını ve maksimum değerleri stdint.h; sisteminizde:

/* Largest integral types.  */
#if __WORDSIZE == 64
typedef long int                intmax_t;
typedef unsigned long int       uintmax_t;
#else
__extension__
typedef long long int           intmax_t;
__extension__
typedef unsigned long long int  uintmax_t;
#endif

/* Minimum for largest signed integral type.  */
# define INTMAX_MIN             (-__INT64_C(9223372036854775807)-1)
/* Maximum for largest signed integral type.  */
# define INTMAX_MAX             (__INT64_C(9223372036854775807))

1
Hayır, onlara ihtiyacınız var, Binary -daha yüksek önceliğe sahiptir <<.
cuonglm

1
@cuonglm ha, zsh üzerinde test etmem için bana hizmet ediyor ... Tekrar teşekkürler!
Stephen Kitt

@cuonglm ve Stephen. Bu iyi bir düzenleme. echo $((1<<63-1))verir 4611686018427387904.

@tomas yup, bash C operatörü önceliğini kullanır, zsh $((1<<63-1))eşittir varsayılan olarak kendi kendine sahiptir $(((1<<63)-1)).
Stephen Kitt

Stephen Kitt ve tomas sayesinde, bunu bilmek güzel, iyi bir soru ve çok kapsamlı bir cevap.
Valentin B.

4

Gönderen CHANGESiçin dosyanın bash2.05b:

j. Kabuk artık makinenin uzun yerine desteklediği en büyük tamsayı boyutunda (intmax_t) aritmetik gerçekleştiriyor.

X86_64 makinelerinde intmax_t, işaretli 64 bitlik tamsayılara karşılık gelir. Böylece -2^63ve arasında anlamlı değerler elde edersiniz 2^63-1. Bu aralığın dışında sadece etrafınızı sararsınız.


Nitpick: arasında -2^63ve 2^63-1dahil.
Nominal Hayvan

4

1024 ile kaydırma bir tane verir, çünkü kaydırma miktarı etkili bir şekilde bit sayısı (64) alınır 1024 === 64 === 0ve bu şekilde 1025 === 65 === 1.

Bir değerden başka bir şeyin kaydırılması 1, biraz daha yüksek bir dönüş olmadığını açıkça gösterir, çünkü daha yüksek bitler, kaydırma değeri (en azından) 64 olmadan önce düşük uca sarılmazlar:

$ printf "%x\n" $(( 5 << 63 )) $(( 5 << 64 ))
8000000000000000
5

Bu davranış sisteme bağlı olabilir. Bash kodu Stephen bağlantılı sağ değeri için herhangi bir kontrol olmaksızın, sadece düz bir kayma gösterir. Doğru hatırlıyorsam, x86 işlemciler, kaydırma değerinin yalnızca alt altı bitini (64 bit modunda) kullanır, bu nedenle davranış doğrudan makine dilinden olabilir. Ayrıca, bit genişliğinden daha fazla kaymaların da C'de açıkça tanımlanmadığını düşünüyorum ( gccbunun için uyarıyor).


2

Biraz kaydırarak bir tamsayı üretir. Ne kadar ileri gidebilirim?

Tamsayı gösterimi tamamlanıncaya kadar (çoğu kabukta varsayılan).
64 bitlik bir tamsayı genellikle konumunda sarar 2**63 - 1.
Bu 0x7fffffffffffffffya 9223372036854775807da aralıkta.

Bu '+1' sayısı negatif olur.

Bu, aynı 1<<63şekilde:

$ echo "$((1<<62)) $((1<<63)) and $((1<<64))"
4611686018427387904 -9223372036854775808 and 1

Bundan sonra işlem tekrarlanır.

$((1<<80000)) $((1<<1022)) $((1<<1023)) $((1<<1024)) $((1<<1025)) $((1<<1026))

Sonuç mod 64, kayma değerinin [a] değerine bağlıdır .

[a] Kimden: Intel® 64 ve IA-32 Mimarlar Yazılım Geliştirici Kılavuzu: Cilt 2 Sayım 5 bit olarak maskelenir (veya 64 bit modunda ve REX.W kullanılıyorsa 6 bit). Sayım aralığı 0 ila 31 (veya 64 bit modu ve REX.W kullanılıyorsa 63) ile sınırlıdır. .

Ayrıca: hatırlamak $((1<<0))olduğunu1

$ for i in 80000 1022 1023 1024 1025 1026; do echo "$((i%64)) $((1<<i))"; done
 0 1
62 4611686018427387904
63 -9223372036854775808
 0 1
 1 2
 2 4

Yani, her şey sayının 64'ün katına ne kadar yakın olduğuna bağlıdır.

Sınırı test etme:

Maksimum pozitif (ve negatif) tamsayı olan testin sağlam yolu, her bir biti sırayla test etmektir. Zaten çoğu bilgisayar için 64 adımdan az, çok yavaş olmayacak.

darbe

İlk önce formun en büyük tamsayısına ihtiyacımız var 2^n(1 bitlik set ve ardından sıfırlar). Bunu, bir sonraki vardiyaya "etrafa sarın" olarak da adlandırılan negatif olana kadar sola kaydırarak yapabiliriz :

a=1;   while ((a>0));  do ((b=a,a<<=1))  ; done

bSonuç nerede : döngüde başarısız olan son vardiyadan önceki değer.

O zaman hangisinin işaretini etkilediğini bulmak için her şeyi denememiz gerekir e:

c=$b;d=$b;
while ((c>>=1)); do
      ((e=d+c))
      (( e>0 )) && ((d=e))
done;
intmax=$d

Maksimum tamsayı ( intmax), öğesinin son değerinden kaynaklanır d.

Negatif tarafta (daha az 0) tüm testleri tekrarlıyoruz, ancak etrafına sarılmadan bir bitin 0 yapılabileceğini test ediyoruz.

Tüm adımların yazdırılmasıyla yapılan bütün bir test şudur (bash için):

#!/bin/bash
sayit(){ printf '%020d 0x%016x\n' "$1"{,}; }
a=1;       while ((a>0)) ; do((b=a,a<<=1))              ; sayit "$a"; done
c=$b;d=$b; while((c>>=1)); do((e=d+c));((e>0))&&((d=e)) ; sayit "$d"; done;
intmax=$d
a=-1;      while ((a<0)) ; do((b=a,a<<=1))              ; sayit "$b"; done;
c=$b;d=$b; while ((c<-1)); do((c>>=1,e=d+c));((e<0))&&((d=e)); sayit "$d"; done
intmin=$d       

printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"

sh

Neredeyse tüm kabuklara çevrilir:

#!/bin/sh
printing=false
sayit(){ "$printing" && printf '%020d 0x%016x\n' "$1" "$1"; }
a=1;       while [ "$a" -gt 0  ];do b=$a;a=$((a<<1)); sayit "$a"; done
c=$b;d=$b; while c=$((c>>1)); [ "$c" -gt 0 ];do e=$((d+c)); [ "$e" -gt 0 ] && d=$e ; sayit "$d"; done;
intmax=$d
a=-1;      while [ "$a" -lt 0  ];do b=$a;a=$((a<<1)); sayit "$b"; done;
c=$b;d=$b; while [ "$c" -lt -1 ];do c=$((c>>1));e=$((d+c));[ "$e" -lt 0 ] && d=$e ; sayit "$d"; done
intmin=$d       

printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"

Birçok kabuk için yukarıdakileri çalıştırmak,
hepsi (bash 2.04 ve mksh hariç 2**63 -1) bu bilgisayarda ( ) 'ye kadar olan değerleri kabul etti .

Att kabuğunun rapor edilmesi ilginçtir :

$ attsh --version
version         sh (AT&T Research) 93u+ 2012-08-01

$((2^63))olsa da, ksh değerlerinde bir hata yazdırdı .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.