Answers:
用十进制表示(dc
用于转换),它对应于999999.98(向下舍入)×256,即 255999994.88,即十六进制的F423FFA.E1。
因此,差异来自于dc
四舍五入的行为:不是计算256×(999999 + 253÷256)(得出255999997),而是将253÷256向下舍入并乘以结果。
dc
是一个任意精度的计算器,这意味着它可以计算出所需的任何精度,但是您必须告诉它是什么。默认情况下,其精度为0,这意味着除法仅生成整数值,并且乘法使用输入中的位数。要设置精度,请使用k
(请记住,精度始终以十进制数字表示,而不管输入或输出基数如何):
10 k
16 d i o
F423FFD 100 / p
F423F.FD0000000
100 * p
F423FFD.000000000
(8位数字的精度就足够了,因为这需要用十进制表示1÷256。)
k
设置时,它仍然会失去精度:10 k 16 d i o F423F.FD p
→ F423F.FA
,所以我必须先放大所有数字,然后才能在中使用它们dc
。无论如何,基本上等于预先准备它们。
dc
仅使用数字来缩放其输入,这对我来说似乎是个错误(因为数字是使用输入的基数计算的,但应用于十进制值)。
20 k 16 d i o 0.3 1 / p
(打印.19999999999999999)。记者了解到,由于操作只是将0.2
通过1
(这在理论上不应该改变的值)。While 20 k 16 d i o 0.3000 1 / p
(正确)打印.30000000000000000
。(续)
问题是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 <<< "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
十进制数字.0009765625
(10)相同的二进制数字(10)。在其他基数中可能不是这种情况,但是基数10是dc和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
零。在shell代码中(用于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 i(2,4,8,16,...)和5,10。
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
同样,读取数字字符串(常量)应使用正确的位数。
分两步:
二进制分数是两个负幂的有限和。
例如:
= 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。然后可以将所有项组合为一个单个值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
将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
供使用,然后直接编写一个解析器!(输入可能有也可能没有小数,并且可以使用其他基数,因此填充量会有所不同。)