Bash 中的大数除法返回错误结果

Bash 中的大数除法返回错误结果

我正在尝试将大量字节转换为千兆字节。

echo $(( 41003021288998461440 / 1073741824 ))

这将返回 3827300985。这是不正确的。正确答案应该是 38187039354。11 位数字对 10 位数字。

使用不同的“scale = 30”或通过 bc 管道不会改变答案。我做错了什么?

作为替代方案,我尝试了这个:

awk -v var1=41003021288998461440 -v var2=1073741824 'BEGIN { print ( var1 / var2 ) }' OFMT='%25g'

它返回“3.8187e+10”,这似乎在数字上是正确的,但我不知道如何不采用科学计数法。 Printf“%12d”没有帮助,因为它似乎无法处理传递参数中的除法。

我怀疑修复 awk 科学计数法问题可能更容易,但我仍然想知道为什么使用 echo 进行长除法会返回完全错误的结果。这非常令人担忧,而且由于我经常以这种方式进行计算,我想知道我需要做什么才能让 echo 准确计算。

我也知道我以前解决过这个问题......但我忘记是怎么解决的了,现在也记不起来了,唉。

答案1

Bash 整数不是任意精度:

评估以固定宽度的整数进行,不检查溢出,但除以 0 会被捕获并标记为错误。

现代系统中有符号整数的可能上限是 2^63:

$ echo $(( 2**63 - 1 ))
9223372036854775807
$ echo $(( 2**63 ))
-9223372036854775808
$ echo $(( 2**62 ))
4611686018427387904 

您的数字太大了(~4x)。如果您想以交互方式执行随机任意精度算术,请使用 Python:

>>> 41003021288998461440 / 1073741824
38187039353.88332

答案2

我发现最好使用 dc 来实现这一点:

V=$(echo "8 k 41003021288998461440 1073741824 / p" | dc)
echo $V

将精度设置为 8,除以接下来的两个值,然后将其从堆栈中弹出。

答案3

最后重建了如何使其准确计算。是的,它确实涉及使用“比例”。

BYTECOUNT=$(echo "scale=0; (( $BYTECOUNT / 1073741824 )) " | bc )

上述方法有效(之前将 BYTECOUNT 设置为非常大的初始数字)。奇怪的是 scale=0 是必需的,因为我认为这应该是默认值,但为了使计算正确,似乎有必要将其明确化。

相关内容