問題
問題は、dc(およびbc)が数値定数を理解する方法です。
たとえば、値(16進数)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
ビットごとに1桁?
浮動小数点数の非常に非直感的な事実は、必要な桁数(ドットの後)がバイナリビットの数(ドットの後)に等しいことです。2進数0.101は、10進数で0.625とまったく同じです。2進数0.0001110001は(正確に)0.1103515625
(10桁の10桁)に等しい
$ bc <<<'scale=30;obase=10;ibase=2; 0.101/1; 0.0001110001/1'; echo ".1234567890"
.625000000000000000000000000000
.110351562500000000000000000000
.1234567890
また、2 ^(-10)のような浮動小数点数の場合、バイナリでは1(セット)ビットのみです:
$ bc <<<"scale=20; a=2^-10; obase=2;a; obase=10; a"
.0000000001000000000000000000000000000000000000000000000000000000000
.00097656250000000000
.0000000001
10 進数(10)と同じ数の2進数(10)を持ってい.0009765625
ます。他の基数ではそうではないかもしれませんが、基数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
です。
また、ドットの後に使用されている文字の数を数えるだけでも、数のスケールを報告(および使用)するための非常に不適切な方法です。(任意のベースの)数値のスケールは、必要なビット数を計算する必要があります。
16進浮動小数点数の2進数。
既知のように、各16進数は4ビットを使用します。したがって、小数点以下の各16進数には4桁の2進数が必要ですが、上記の(奇数?)事実のために4桁の10進数も必要です。
したがって、次のような数値で0.FD
は、8桁の10進数が正しく表現される必要があります。
$ bc <<<'obase=10;ibase=16;a=0.FD000000; scale(a);a;a*100'
8
.98828125
253.00000000
ゼロを追加
数学は簡単です(16進数の場合):
h
ドットの後の16進数()の数を数えます。
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
16進浮動小数点数を内部10進表現に変換するために、必要な桁数を上記で計算された数()と一致させることができます。または、他の基数に対する他の関数(そのような他の基数の基数10(bcおよびdcの内部)に関して桁数が有限であると仮定します)。2 i(2,4,8,16、...)および5,10など。
posix
posix仕様には、次のように記載されています(bcの場合、どのdcが基づいているか)。
内部計算は、入力ベースおよび出力ベースに関係なく、指定された10進数の桁まで、10進数のように実行されます。
しかし、「…指定された数の10進数」。「... 10進数の内部計算」に影響を与えることなく、「...数値定数を表すのに必要な10進数の桁数」(上記のように)と理解できます。
なぜなら:
bc <<<'scale=50;obase=16;ibase=16; a=0.FD; a+1'
1.FA
bcは、上記のように実際には50(指定された10進数の桁数)を使用していません。
分割された場合にのみ変換されます(0.FD
50桁に拡張する前に定数を読み取るために2のスケールを使用するため、依然として正しくありません):
$ 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
繰り返しますが、数値文字列(定数)を読み取るには、正しいビット数を使用する必要があります。
数学の証明
2つのステップで:
バイナリ分数は、a / 2 nとして記述できます。
2進小数は、2の負のべき乗の有限和です。
例えば:
= 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進小数では、最後のビットの値は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 の正の分子指数で2 nになります。すべての項は、単一の値a / 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
すべての2進小数は、同じ桁数の10進小数であることが示されています。
dc
使用する数値をマッサージするために、パーサーを直接書くために、より多くのコードが必要になると思います!(入力には小数がある場合とない場合があり、他の