สร้างจำนวนเต็มโดยขยับบิต ฉันจะไปได้ไกลแค่ไหน?
จนกระทั่งการเป็นตัวแทนจำนวนเต็มล้อมรอบ (ค่าเริ่มต้นในเชลล์ส่วนใหญ่)
64 2**63 - 1
บิตจำนวนเต็มมักจะล้อมรอบที่
นั่นคือ0x7fffffffffffffff
หรือ9223372036854775807
ในเดือนธันวาคม
หมายเลขนั้น '+1' จะกลายเป็นลบ
นั่นเป็นเช่นเดียวกับ1<<63
:
$ echo "$((1<<62)) $((1<<63)) and $((1<<64))"
4611686018427387904 -9223372036854775808 and 1
หลังจากนั้นกระบวนการจะทำซ้ำอีกครั้ง
$((1<<80000)) $((1<<1022)) $((1<<1023)) $((1<<1024)) $((1<<1025)) $((1<<1026))
ผลที่ได้ขึ้นอยู่กับmod 64
ของมูลค่าขยับ[เป็น]
[a] จาก: คู่มือผู้พัฒนาซอฟท์แวร์สถาปัตยกรรมซอฟต์แวร์Intel® 64 และ IA-32: เล่มที่ 2จำนวนมาสก์เป็น 5 บิต (หรือ 6 บิตหากใช้โหมด 64 บิตและ REX.W) ช่วงการนับนั้น จำกัด ไว้ที่ 0 ถึง 31 (หรือ 63 ถ้าใช้โหมด 64 บิตและ REX.W) .
ยัง: จำไว้ว่า$((1<<0))
เป็น1
$ 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
ดังนั้นทุกอย่างขึ้นอยู่กับว่าหมายเลขนั้นใกล้เคียงกับผลคูณของ 64
ทดสอบขีด จำกัด :
วิธีที่แข็งแกร่งในการทดสอบซึ่งเป็นจำนวนเต็มบวก (และลบ) สูงสุดคือการทดสอบทีละน้อย มันน้อยกว่า 64 ขั้นตอนสำหรับคอมพิวเตอร์ส่วนใหญ่อย่างไรก็ตามมันจะไม่ช้าเกินไป
ทุบตี
ก่อนอื่นเราต้องการจำนวนเต็มที่มากที่สุดของแบบฟอร์ม2^n
(1 bit set ตามด้วยศูนย์) เราสามารถทำได้โดยเลื่อนไปทางซ้ายจนกว่าการเปลี่ยนครั้งต่อไปจะทำให้จำนวนติดลบเรียกอีกอย่างว่า "ล้อมรอบ":
a=1; while ((a>0)); do ((b=a,a<<=1)) ; done
b
ผลลัพธ์อยู่ที่ไหน: ค่าก่อนการเปลี่ยนครั้งล่าสุดที่ทำให้ลูปล้มเหลว
จากนั้นเราต้องลองทุกบิตเพื่อดูว่าอันไหนมีผลต่อสัญลักษณ์ของe
:
c=$b;d=$b;
while ((c>>=1)); do
((e=d+c))
(( e>0 )) && ((d=e))
done;
intmax=$d
จำนวนเต็มสูงสุด ( intmax
) d
เป็นผลมาจากค่าสุดท้ายของ
ทางด้านลบ (น้อยกว่า0
) เราทำการทดสอบทั้งหมดซ้ำ แต่การทดสอบเมื่อสามารถทำ 0 ได้โดยไม่ต้องพัน
การทดสอบทั้งหมดพร้อมพิมพ์ทุกขั้นตอนคือ (สำหรับทุบตี):
#!/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"
ดวลจุดโทษ
แปลเป็นเกือบทุกเชลล์:
#!/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"
ทำงานข้างต้นสำหรับเชลล์จำนวนมากค่า
ทั้งหมด (ยกเว้น bash 2.04 และ mksh) ยอมรับได้ถึง ( 2**63 -1
) ในคอมพิวเตอร์เครื่องนี้
เป็นที่น่าสนใจที่จะรายงานว่าเปลือก att :
$ attsh --version
version sh (AT&T Research) 93u+ 2012-08-01
พิมพ์ข้อผิดพลาดเกี่ยวกับค่าที่$((2^63))
ไม่ใช่ ksh