通过 BigDecimal 转换为浮点数的适当比例

Appropriate scale for converting via BigDecimal to floating point

我写了一个任意精度的有理数 class,它需要提供一种转换为浮点数的方法。这可以通过 BigDecimal 直接完成:

return new BigDecimal(num).divide(new BigDecimal(den), 17, RoundingMode.HALF_EVEN).doubleValue();

但这需要在除以十进制数时为比例参数指定一个值。我选择 17 作为初始猜测,因为这大约是双精度浮点数的精度,但我不知道这是否真的正确。

正确使用的数字是多少,定义为使它变大不会使答案更准确的最小数字?

简介

没有足够的精度。

问题中提出的问题等价于:

  • 什么精度p保证将任意有理数x转换为p十进制数和然后到浮点数产生最接近 x 的浮点数(或者,在平局的情况下,两个最接近的 x 中的一个)?

要看出这是等价的,请观察问题 returns num/div 中显示的 BigDecimal 除以选定的小数位数。然后问题是增加小数位数是否可以提高结果的准确性。显然,如果有一个比结果更接近 x 的浮点数,则可以提高精度。因此,我们问的是需要多少小数位才能保证获得最接近的浮点数(或并列的两个浮点数之一)。

由于BigDecimal提供了舍入方法的选择,我会考虑其中的任何一种是否足够。为了转换为浮点数,我假设使用了舍入到最近的偶数(在转换为 DoubleFloat 时似乎使用了 BigDecimal)。我使用 IEEE-754 binary64 格式给出证明,Java 用于 Double,但通过更改 252[=89,该证明适用于任何二进制浮点格式=] 下面用于 2w-1,其中 w 是有效位数中的位数.

证明

BigDecimal 除法的参数之一是舍入方法。 Java’s BigDecimal has several rounding methods。我们只需要考虑三个,ROUND_UP、ROUND_HALF_UP和ROUND_HALF_EVEN。通过使用各种对称性,其他人的论点类似于下面的论点。

下面,假设我们使用任何大精度p转换为十进制。即p为转换结果的小数位数

m为有理数252+1+½−10 p。与m相邻的两个binary64数是252+1和252+2。 m更接近第一个,所以这就是我们需要将m先转换为十进制再转换为浮点数的结果。

在十进制中,m是4503599627370497.4999…,其中有p−1尾随9。当使用 ROUND_UP、ROUND_HALF_UP 或 ROUND_HALF_EVEN 四舍五入到 p 有效数字时,结果为 4503599627370497.5 = 252+1+½。 (认识到,在发生舍入的位置,有 16 个尾随 9 被舍弃,实际上是相对于舍入位置的 .9999999999999999 的一小部分。在 ROUND_UP 中,任何非零舍弃量都会导致舍入。在 ROUND_HALF_UP 和 ROUND_HALF_EVEN,在该位置丢弃的数量大于 ½ 会导致四舍五入。)

252+1+½ 与相邻的 binary64 数字 252+1 和 2 同样接近52+2,所以四舍五入法产生 252+2.

因此,结果是252+2,这不是最接近m.

的binary64值

因此,有限精度 p 不足以正确舍入所有有理数。