Vấn đề
Vấn đề là cách mà dc (và bc) hiểu các hằng số.
Ví dụ: giá trị (ở dạng hex) 0.3
(chia cho 1) được chuyển đổi thành giá trị gần với0.2
$ dc <<<"20k 16 d i o 0.3 1 / p"
.199999999999999999999999999
Trong thực tế, hằng số đồng bằng 0.3
cũng được thay đổi:
$ dc <<<"20 k 16 d i o 0.3 p"
.1
Có vẻ như đó là một cách kỳ lạ, nhưng nó không phải là (muộn hơn).
Thêm nhiều số không làm cho câu trả lời tiếp cận giá trị chính xác:
$ 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
Giá trị cuối cùng là chính xác và sẽ giữ chính xác cho dù có thể thêm nhiều số không.
$ dc <<<"20 k 16 d i o 0.30000000 p"
.3000000
Vấn đề cũng có trong 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
Một chữ số trên mỗi bit?
Một thực tế không trực quan cho các số dấu phẩy động là số chữ số được yêu cầu (sau dấu chấm) bằng với số bit nhị phân (cũng sau dấu chấm). Số nhị phân 0.101 chính xác bằng 0,625 trong số thập phân. Số nhị phân 0,0001110001 là (chính xác) bằng 0.1103515625
(mười chữ số thập phân)
$ bc <<<'scale=30;obase=10;ibase=2; 0.101/1; 0.0001110001/1'; echo ".1234567890"
.625000000000000000000000000000
.110351562500000000000000000000
.1234567890
Ngoài ra, đối với số dấu phẩy động như 2 ^ (- 10), trong nhị phân chỉ có một bit (bộ):
$ bc <<<"scale=20; a=2^-10; obase=2;a; obase=10; a"
.0000000001000000000000000000000000000000000000000000000000000000000
.00097656250000000000
Có cùng số chữ số nhị phân .0000000001
(10) là chữ số thập phân.0009765625
(10). Đó có thể không phải là trường hợp trong các cơ sở khác nhưng cơ sở 10 là biểu diễn bên trong của các số ở cả dc và bc và do đó là cơ sở duy nhất mà chúng ta thực sự cần quan tâm.
Bằng chứng toán học là ở cuối câu trả lời này.
quy mô bc
Số chữ số sau dấu chấm có thể được tính bằng scale()
mẫu hàm tích hợp bc:
$ bc <<<'obase=16;ibase=16; a=0.FD; scale(a); a; a*100'
2
.FA
FA.E1
Như được hiển thị, 2 chữ số là không đủ để đại diện cho hằng số 0.FD
.
Và, ngoài ra, chỉ cần đếm số lượng ký tự được sử dụng sau dấu chấm là một cách rất không chính xác để báo cáo (và sử dụng) tỷ lệ của số. Tỷ lệ của một số (trong bất kỳ cơ sở nào) sẽ tính toán số lượng bit cần thiết.
Chữ số nhị phân trong một hình nổi hex.
Như đã biết, mỗi chữ số hex sử dụng 4 bit. Do đó, mỗi chữ số hex sau dấu thập phân yêu cầu 4 chữ số nhị phân, do thực tế (lẻ?) Ở trên cũng yêu cầu 4 chữ số thập phân.
Do đó, một số như thế 0.FD
sẽ yêu cầu 8 chữ số thập phân được thể hiện chính xác:
$ bc <<<'obase=10;ibase=16;a=0.FD000000; scale(a);a;a*100'
8
.98828125
253.00000000
Thêm số không
Toán học rất đơn giản (đối với số hex):
- Đếm số chữ số hex (
h
) sau dấu chấm.
- Nhân
h
với 4.
- Thêm
h×4 - h = h × (4-1) = h × 3 = 3×h
số không.
Trong mã shell (cho 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
Mà sẽ in (chính xác cả trong dc và bc):
$ sh ./script
F423F.FD000000
F423FFD.0000000
F423FFD.0000000
Trong nội bộ, bc (hoặc dc) có thể làm cho số chữ số được yêu cầu khớp với số được tính ở trên ( 3*h
) để chuyển đổi số float thành biểu diễn thập phân nội bộ. Hoặc một số hàm khác cho các cơ sở khác (giả sử số chữ số là hữu hạn so với cơ sở 10 (nội bộ của bc và dc) trong cơ sở khác đó). Thích 2 tôi (2,4,8,16, ...) và 5,10.
tích cực
Đặc tả posix nói rằng (đối với bc, dc dựa trên):
Việc tính toán nội bộ phải được tiến hành như thể ở dạng thập phân, bất kể cơ sở đầu vào và đầu ra, với số chữ số thập phân được chỉ định.
Nhưng "Số lượng chữ số thập phân đã chỉ định." có thể được hiểu là "Sắp xếp số chữ số thập phân cần thiết để biểu diễn hằng số" (như được mô tả ở trên) mà không ảnh hưởng đến "tính toán nội bộ thập phân"
Bởi vì:
bc <<<'scale=50;obase=16;ibase=16; a=0.FD; a+1'
1.FA
bc không thực sự sử dụng 50 ("số chữ số thập phân được chỉ định") như đã đặt ở trên.
Chỉ khi được chia, nó mới được chuyển đổi (vẫn không chính xác vì nó sử dụng thang đo 2 để đọc hằng số 0.FD
trước khi mở rộng thành 50 chữ số):
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD/1; a'
.FAE147AE147AE147AE147AE147AE147AE147AE147A
Tuy nhiên, đây là chính xác:
$ bc <<<'scale=50;obase=16;ibase=16; a=0.FD000000/1; a'
.FD0000000000000000000000000000000000000000
Một lần nữa, đọc các chuỗi số (hằng số) nên sử dụng đúng số bit.
Chứng minh toán
Trong hai bước:
Một phần nhị phân có thể được viết là a / 2 n
Một phần nhị phân là tổng hữu hạn của lũy thừa âm của hai.
Ví dụ:
= 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 = (đã xóa số không)
Trong một phần nhị phân của n bit, bit cuối cùng có giá trị 2 -n hoặc 1/2 n . Trong ví dụ này: 2 -11 hoặc 1/2 11 .
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (có nghịch đảo)
Nói chung, mẫu số có thể trở thành 2 n với số mũ dương là hai. Tất cả các thuật ngữ sau đó có thể được kết hợp thành một giá trị duy nhất a / 2 n . Ví dụ này:
= 2 8 /2 11 + 2 7 /2 11 + 2 5 /2 11 + 2 3 /2 11 + 2 2 /2 11 + 1/2 11 = (thể hiện với 2 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (trích hệ số chung)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (chuyển đổi thành giá trị)
= 429/2 11
Mọi phân số nhị phân có thể được biểu thị bằng b / 10 n
Nhân a / 2 n với 5 n
/ 5 n , nhận (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n , trong đó b = a × 5 n . Nó có n chữ số.
Ví dụ, chúng tôi có:
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0.20947265625
Nó đã được chỉ ra rằng mỗi phân số nhị phân là một phân số thập phân có cùng số chữ số.
dc
để sử dụng sau đó chỉ cần viết một trình phân tích cú pháp trực tiếp! (Đầu vào có thể có hoặc không có số thập phân và có thể ở các cơ sở khác, vì vậy số lượng phần đệm thay đổi.)