我想dc
用十六进制点处理一些基数为 16 的数字,但我遇到了精度问题。例如,下面我乘以F423F.FD
,100
都是十六进制。预期的答案是F423FFD
,而是给出F423FFA.E1
,接近但即使在四舍五入之后也不够准确。
$ dc
16 d i o F423F.FD 100 * p
F423FFA.E1
我读到那dc
是一个无限精度的计算器,无论如何这都不是一个大数字。有什么我做错了吗?
感谢您的回答。考虑到 的问题dc
,我硬着头皮写了自己的解析器来解析其他基数的实数。如果有人对代码感兴趣,我可以在这里发布。
用十进制表示(
dc
用来转换),对应999999.98(四舍五入)×256,即255999994.88,即十六进制的F423FFA.E1。因此差异来自
dc
的舍入行为:它不是计算 256 × (999999 + 253 ÷ 256),而是将 253 ÷ 256 向下舍入并乘以结果。dc
是一个任意精度计算器,这意味着它可以计算到你想要的任何精度,但你必须告诉它那是什么。默认情况下,它的精度为 0,这意味着除法只产生整数值,而乘法使用输入中的位数。要设置精度,请使用k
(并记住精度始终以十进制数字表示,无论输入或输出基数如何):(8 位的精度就足够了,因为这是您需要用十进制表示 1 ÷ 256。)
请注意,仅打印原始数字表明它是四舍五入的:
您可以通过添加大量尾随零来解决它以提高精度:
问题
问题在于 dc(和 bc)理解数字常量的方式。
例如,值(十六进制)
0.3
(除以 1)被转换为接近于0.2
事实上,普通常量
0.3
也发生了变化:这似乎是一种奇怪的方式,但事实并非如此(稍后会详细介绍)。
添加更多零会使答案接近正确值:
最后一个值是准确的,并且无论添加多少零都将保持准确。
问题也存在于 bc 中:
一位数字?
浮点数非常不直观的事实是所需的位数(在点之后)等于二进制位数(也在点之后)。二进制数 0.101 正好等于十进制的 0.625。二进制数 0.0001110001 (完全)等于
0.1103515625
(十进制数字)此外,对于像 2^(-10) 这样的浮点数,它在二进制中只有一个(设置)位:
二进制位数
.0000000001
(10) 与十进制位数.0009765625
(10) 相同。在其他基数中可能不是这种情况,但基数 10 是 dc 和 bc 中数字的内部表示,因此是我们真正需要关心的唯一基数。数学证明在这个答案的末尾。
公元前规模
点后面的位数可以用内置函数
scale()
形式 bc 计算:如图所示,2 位数字不足以表示常数
0.FD
。而且,仅计算点后使用的字符数是报告(和使用)数字比例的一种非常不正确的方法。数字的小数位数(任何基数)应计算所需的位数。
十六进制浮点数中的二进制数字。
众所周知,每个十六进制数字使用 4 位。因此,小数点后的每个十六进制数字需要 4 个二进制数字,由于上面的(奇数?)事实也需要 4 个十进制数字。
因此,像这样的
0.FD
数字需要 8 个十进制数字才能正确表示:添加零
数学很简单(对于十六进制数字):
h
) 的数量。h
4。h×4 - h = h × (4-1) = h × 3 = 3×h
零。在 shell 代码中(对于 sh):
哪个将打印(在 dc 和 bc 中都正确):
在内部,bc(或 dc)可以使所需的位数与上面计算的数字(
3*h
)相匹配,以将十六进制浮点数转换为内部十进制表示。或其他基数的其他函数(假设在这种其他基数中,位数相对于基数 10(bc 和 dc 的内部)是有限的)。像 2 i (2,4,8,16,...) 和 5,10。正则表达式
posix 规范指出(对于 bc,dc 基于):
但是“……指定的小数位数。” 可以理解为“……表示数字常量所需的十进制位数”(如上所述)而不影响“十进制内部计算”
因为:
bc 并没有真正使用上面设置的 50(“指定的小数位数”)。
只有在除以它时才会被转换(仍然不正确,因为它使用 2 的比例来读取常数
0.FD
,然后再将其扩展到 50 位):但是,这是准确的:
同样,读取数字字符串(常量)应该使用正确的位数。
数学证明
分两步:
二进制分数可以写成 a/2 n
二元分数是二的负幂的有限和。
例如:
= 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
每个二进制分数都可以表示为 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
已经证明,每个二进制分数都是具有相同位数的十进制分数。