非常大的数字正弦值的标准

9

我正在使用TeX编写一个(几乎)符合IEEE 854标准的浮点数实现(该语言仅支持32位整数)。该标准仅规定了+-*/、比较、余数和sqrt的结果:对于这些操作,结果应与将精确结果舍入为可表示数字(根据舍入模式)相同。

我记得IEEE指定超越函数(sinexp等)应产生准确结果(在默认的四舍五入模式下,它们应输出围绕精确结果的两个可表示数字之一)。计算小数的正弦值相当简单:通过多次2*pi的移位获得[0,2*pi]范围内的数字,然后进行更多的工作将范围缩小到[0,pi/4],并使用泰勒级数。

现在假设我想计算sin(1e300)。为此,我需要找到1e300对2*pi的模数。这需要知道pi的300(316?)个小数位,因为只有16个小数位的结果没有任何意义(特别是它不应准确)。

是否有标准规定sin(1e300)和类似的非常大的数字的结果应该是什么?

其他浮点数实现如何处理?


1
+1 很好的解释一个有趣的问题(我以前没有想过) - pavium
2个回答

11

没有标准要求超越函数进行精确舍入。IEEE-754(2008)建议这些函数进行正确舍入,但并不强制要求。

大多数优秀的数学库努力提供整个范围内的精确结果(是的,甚至对于sin()和类似的难题输入)。如您所说,这需要库知道比最大可表示数字中的位数更多的π位数。这称为“无限π”参数缩减。

针对@spraff提出的问题,优秀的数学库采取的观点是假定输入是无限精确的(即函数应该表现得好像输入总是准确地表示)。可以争论这是否是一个合理的立场,但是这是基本上所有优秀的数学库的工作假设。

所有这些都说了,有很多库采用了简单的方法,并使用“有限π”缩减,这基本上将诸如sin()之类的函数视为π是一个可表示的有限数字。事实证明,这对于大多数用途并不真的造成任何麻烦,而且肯定更容易实现。


2
“无限π参数缩减”在处理大参数时很有帮助,但处理非常大的参数时就不再适用了。对于库来说,假设其输入是准确的是可以的,但当输入+epsilon==input时,这就不再重要了。 - spraff
2
@spraff:就反向误差分析而言,这并不重要。但是,在使用其他形式的误差分析时,这很重要,并且这是最好的数学库所坚持的标准。例如,请参阅K.C. Ng的“Good to the Last Bit”进行讨论。 - Stephen Canon
在你的第三段中,你可以指出好的库之所以这样做是因为IEEE-754(2008)认为浮点数是无限精确的。你能否举例说明“好的数学库”与那些“采取简单方法”的区别?我认为这会对你的答案有所帮助。采用简单方法不会引起太多麻烦的原因之一是它不会违反三角恒等式,例如sin(2x)= 2 * sin(x)* cos(x)。 - Bruno Le Floch
1
@Bruno:如果你只需要两个实现匹配,你可以在两个实现中都使用相同的有限圆周率近似值。只要你在两个实现中进行算术运算时小心保持一致,这应该就足够了。 - Stephen Canon
5
@BrunoLeFloch .NET框架的库并不算“好”。我刚刚发现,当参数超过Pow(2, 63)时,Sin方法会将其参数原封不动地返回!因此,虽然 Sin(9.223372036854775E + 18) == 0.98734189131435146 可能不太精确,但 Sin(9.223372036854776E + 18) 大约是 9.22E+18... - Jeppe Stig Nielsen
显示剩余4条评论

0

如果你在处理如此大的数字,当然会出现精度不足的问题:

#include <iostream>
#include <math.h>

int main () {
    long double i = 1;
    std :: cout << sin (i) << "\n" << sin (i+0.1) << "\n";
    i = pow (10, 300);
    std :: cout << sin (i) << "\n" << sin (i+0.1);
}

输出:

0.841471

0.891207

-0.817882

-0.81788

如果不能准确地表示输入,就不能准确地表示输出。从 pi * pow(10, int(log_10(n/pi)))或其他任何数值中减去将会使“小”的n更糟糕,但当n足够大时,你只是在噪声中添加噪声,这已经不再重要了。


3
简单来说,1e300 + 2pi = 1e300 - 2pi = 1e300,因此由1e300所代表的可能的精确值在应用于sine函数时可以得到任何值? - Damien_The_Unbeliever
3
当将1e300四舍五入为最接近的可表示的“double”值时,该舍入涵盖了大约长度为1e284的区间。因此,当我们将1e300舍入为可表示的“double”时,我们会“穿过”非常多的2pi长度周期。因此,计算sin函数是相当无意义的。不过,从数学上讲,在我们将参数舍入为可表示值之后,仍然可以计算该确切参数的sin值。要这样做需要对2pi的表示具有很高的精度。正如我在对其他答案的评论中所说,在.NET中,Sin(1e300) == 1e300 - Jeppe Stig Nielsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接