2

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

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

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

什么是正确的数字,定义为最小的数字,使得它变得更大不会使答案更准确?

4

1 回答 1

2

介绍

没有有限的精度就足够了。

问题中提出的问题相当于:

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

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

由于BigDecimal提供了舍入方法的选择,我将考虑其中任何一个是否足够。对于转换为浮点数,我假设使用 round-to-nearest-ties-to-even (转换为orBigDecimal时似乎使用)。我使用 IEEE-754 binary64 格式提供证明,Java 用于,但证明适用于任何二进制浮点格式,方法是将下面使用的 2 52更改为 2 w -1,其中w是有效数字。DoubleFloatDouble

证明

除法的参数之一BigDecimal是舍入方法。JavaBigDecimal有几种舍入方法。我们只需要考虑三个,ROUND_UP、ROUND_HALF_UP和ROUND_HALF_EVEN。通过使用各种对称性,其他参数类似于下面的参数。

在下文中,假设我们使用任何大精度p转换为十进制。也就是说,p是转换结果中的小数位数。

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

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

2 52 +1+½ 与相邻的二进制 64 数字 2 52 +1 和 2 52 +2同样接近,因此四舍五入法产生 2 52 +2。

因此,结果是 2 52 +2,这不是最接近m的 binary64 值。

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

于 2019-10-11T01:20:25.440 回答