ปัญหา
ปัญหาคือวิธีที่ dc (และ bc) เข้าใจค่าคงที่ตัวเลข
ตัวอย่างเช่นค่า (เป็นฐานสิบหก) 0.3
(หารด้วย 1) ถูกแปลงเป็นค่าใกล้เคียง0.2
$ dc <<<"20k 16 d i o 0.3 1 / p"
.199999999999999999999999999
อันที่จริงค่าคงที่ธรรมดา0.3
ก็เปลี่ยนไปเช่นกัน
$ dc <<<"20 k 16 d i o 0.3 p"
.1
ดูเหมือนว่ามันจะแปลก แต่ก็ไม่ใช่ (อีกต่อไป)
การเพิ่มศูนย์เพิ่มเติมทำให้คำตอบเข้าใกล้ค่าที่ถูกต้อง:
$ dc <<<"20 k 16 d i o 0.30 p"
.2E
$ dc <<<"20 k 16 d i o 0.300 p"
.2FD
$ dc <<<"20 k 16 d i o 0.3000 p"
.3000
ค่าสุดท้ายเป็นค่าที่แน่นอนและจะยังคงอยู่อย่างแน่นอนไม่ว่าจะมีการเติมศูนย์เพิ่มอีกก็ตาม
$ dc <<<"20 k 16 d i o 0.30000000 p"
.3000000
ปัญหายังมีอยู่ใน bc:
$ bc <<< "scale=20; obase=16; ibase=16; 0.3 / 1"
.19999999999999999
$ bc <<< "scale=20; obase=16; ibase=16; 0.30 / 1"
.2E147AE147AE147AE
$ bc <<< "scale=20; obase=16; ibase=16; 0.300 / 1"
.2FDF3B645A1CAC083
$ bc <<< "scale=20; obase=16; ibase=16; 0.3000 / 1"
.30000000000000000
หนึ่งหลักต่อบิต?
ข้อเท็จจริงที่ไม่เข้าใจง่ายมากสำหรับตัวเลขจำนวนจุดลอยตัวคือจำนวนของตัวเลขที่ต้องการ (หลังจุด) เท่ากับจำนวนบิตบิต (หลังจากจุด) เลขฐานสอง 0.101 เท่ากับ 0.625 ในทศนิยม เลขฐานสอง 0.0001110001 คือ (ตรง) เท่ากับ0.1103515625
(สิบหลักสิบ)
$ bc <<<'scale=30;obase=10;ibase=2; 0.101/1; 0.0001110001/1'; echo ".1234567890"
.625000000000000000000000000000
.110351562500000000000000000000
.1234567890
นอกจากนี้สำหรับจำนวนจุดลอยตัวเช่น 2 ^ (- 10) ซึ่งเป็นเลขฐานสองมีบิตเดียว (ชุด):
$ bc <<<"scale=20; a=2^-10; obase=2;a; obase=10; a"
.0000000001000000000000000000000000000000000000000000000000000000000
.00097656250000000000
มีเลขฐานสองจำนวนเดียวกัน.0000000001
(10) เป็นเลขฐานสิบ.0009765625
(10) นั่นอาจไม่ใช่ในฐานอื่น ๆ แต่ base 10 คือการแสดงตัวเลขภายในทั้ง dc และ bc ดังนั้นจึงเป็นเพียงฐานเดียวที่เราต้องใส่ใจ
หลักฐานทางคณิตศาสตร์อยู่ท้ายคำตอบนี้
ระดับ bc
จำนวนหลักหลังจากจุดสามารถนับได้ด้วยฟังก์ชั่นในตัวscale()
bc:
$ bc <<<'obase=16;ibase=16; a=0.FD; scale(a); a; a*100'
2
.FA
FA.E1
ตามที่ปรากฏ 2 0.FD
หลักไม่เพียงพอที่จะเป็นตัวแทนอย่างต่อเนื่อง
และเพียงแค่นับจำนวนตัวอักษรที่ใช้หลังจากจุดเป็นวิธีที่ไม่ถูกต้องมากในการรายงาน (และใช้) สเกลของตัวเลข สเกลของตัวเลข (ในฐานใด ๆ ) ควรคำนวณจำนวนบิตที่ต้องการ
เลขฐานสองในทศนิยมหกเหลี่ยม
ตามที่ทราบกันแล้วเลขฐานสิบหกแต่ละตัวใช้ 4 บิต ดังนั้นแต่ละเลขฐานสิบหกหลังจุดทศนิยมจำเป็นต้องมีเลขฐานสอง 4 หลักซึ่งเนื่องจากข้อเท็จจริง (คี่?) ข้างต้นจึงต้องมีตัวเลขทศนิยม 4 หลัก
ดังนั้นตัวเลขที่คล้ายกัน0.FD
จะต้องมีตัวเลขทศนิยม 8 หลักในการแสดงอย่างถูกต้อง:
$ bc <<<'obase=10;ibase=16;a=0.FD000000; scale(a);a;a*100'
8
.98828125
253.00000000
เพิ่มศูนย์
คณิตศาสตร์ตรงไปตรงมา (สำหรับเลขฐานสิบหก):
- นับจำนวนตัวเลขฐานสิบหก (
h
) หลังจุด
- คูณ
h
ด้วย 4
- เพิ่ม
h×4 - h = h × (4-1) = h × 3 = 3×h
ศูนย์
ในรหัสเชลล์ (สำหรับ sh):
a=F423F.FD
h=${a##*.}
h=${#h}
a=$a$(printf '%0*d' $((3*h)) 0)
echo "$a"
echo "obase=16;ibase=16;$a*100" | bc
echo "20 k 16 d i o $a 100 * p" | dc
สิ่งที่จะพิมพ์ (ถูกต้องทั้งใน dc และ bc):
$ sh ./script
F423F.FD000000
F423FFD.0000000
F423FFD.0000000
ภายใน bc (หรือ dc) สามารถทำให้จำนวนตัวเลขที่ต้องการตรงกับจำนวนที่คำนวณข้างต้น ( 3*h
) เพื่อแปลงเลขฐานสิบหกให้เป็นทศนิยมภายใน หรือฟังก์ชั่นอื่น ๆ สำหรับฐานอื่น ๆ (สมมติว่าจำนวนหลักนั้น จำกัด ในความสัมพันธ์กับฐาน 10 (ภายในของ bc และ dc) ในฐานอื่น ๆ ดังกล่าว) ชอบ 2 ฉัน (2,4,8,16, ... ) และ 5,10
POSIX
ข้อมูลจำเพาะ posix ระบุว่า (สำหรับ bc ซึ่ง dc ยึดตาม):
การคำนวณภายในจะต้องดำเนินการเสมือนเป็นทศนิยมโดยไม่คำนึงถึงฐานอินพุตและเอาต์พุตตามจำนวนหลักทศนิยมที่ระบุ
แต่ "... คือจำนวนทศนิยมที่ระบุ" สามารถเข้าใจได้ว่าเป็น "... จำนวนทศนิยมที่ต้องการเพื่อแสดงค่าคงที่ตัวเลข" (ตามที่อธิบายไว้ข้างต้น) โดยไม่มีผลต่อ "การคำนวณภายในทศนิยม"
เพราะ:
bc <<<'scale=50;obase=16;ibase=16; a=0.FD; a+1'
1.FA
bc ไม่ได้ใช้ 50 ("จำนวนทศนิยมที่ระบุ") ตามที่กำหนดไว้ด้านบน
เฉพาะเมื่อแบ่งแล้วจะถูกแปลง (ยังไม่ถูกต้องเนื่องจากใช้มาตราส่วน 2 เพื่ออ่านค่าคงที่0.FD
ก่อนที่จะขยายเป็น 50 หลัก):
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD/1; a'
.FAE147AE147AE147AE147AE147AE147AE147AE147A
อย่างไรก็ตามนี่แน่นอน:
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD000000/1; a'
.FD0000000000000000000000000000000000000000
อีกครั้งการอ่านสตริงตัวเลข (ค่าคงที่) ควรใช้จำนวนบิตที่ถูกต้อง
หลักฐานทางคณิตศาสตร์
ในสองขั้นตอน:
เศษส่วนไบนารีสามารถเขียนเป็น a / 2 n
เศษส่วนไบนารีคือผลรวมที่แน่นอนของกำลังลบของสอง
ตัวอย่างเช่น:
= 0.00110101101 =
= 0. 0 0 1 1 0 1 0 1 1 0 1
= 0 + 0 × 2 -1 + 0 × 2 -2 + 1 × 2 -3 + 1 × 2 -4 + 0 × 2 -5 + 1 × 2 -6 + 0 × 2 -7 + 1 × 2 -8 + 1 × 2 -9 + 0 × 2 -10 + 1 × 2 -11
= 2 -3 + 2 -4 + 2 -6 + 2 -8 + 2 -9 + 2 -11 = (โดยลบศูนย์)
ในส่วนของไบนารีบิต n บิตสุดท้ายมีค่าเป็น 2 -nหรือ 1/2 n ในตัวอย่างนี้: 2 -11หรือ 1/2 11
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (มีค่าผกผัน)
โดยทั่วไปตัวส่วนอาจกลายเป็น 2 nโดยมีเลขชี้กำลังเป็นจำนวนเต็มบวก เงื่อนไขทั้งหมดแล้วสามารถนำมารวมเป็นค่าเดียว / 2 n สำหรับตัวอย่างนี้:
= 2 8 /2 11 + 2 7 /2 11 + 2 5 /2 11 + 2 3 /2 11 + 2 2 /2 11 + 1/2 11 = (แสดงมี 2 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (การแยกปัจจัยทั่วไป)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (แปลงเป็นค่า)
= 429/2 11
เศษส่วนไบนารีทุกส่วนสามารถแสดงเป็น b / 10 n
คูณ a / 2 nด้วย 5 n
/ 5 n , รับ (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n , โดยที่ b = a × 5 n . มันมีตัวเลข n
ตัวอย่างเช่นเรามี:
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0.20947265625
มันแสดงให้เห็นว่าทุกเศษส่วนไบนารีเป็นเศษทศนิยมที่มีจำนวนหลักเดียวกัน
dc
เพื่อใช้แล้วเพียงแค่เขียนโปรแกรมแยกวิเคราะห์โดยตรง! (อินพุตอาจมีหรือไม่มีทศนิยมและสามารถอยู่ในฐานอื่นดังนั้นจำนวนของการแพ็ดจะแตกต่างกันไป)