为什么 python 在划分 sin() 的结果时会给出很大的错误

Why is python giving large errors when dividing the results of sin()

我试图为我找到的一个方程找到 n 的前十个整数输入的值:x = sin(2π / n) / sin(π / n)。我为找到它们而编写的代码得到的输出非常令人惊讶:

>>> for i in range(1, 11):
        print math.sin(2 * math.pi / i) / math.sin(math.pi / i)

-2.0
1.22464679915e-16
1.0
1.41421356237
1.61803398875
1.73205080757
1.8019377358
1.84775906502
1.87938524157
1.90211303259

我知道会有舍入误差,因此下面的结果一点也不让我吃惊:

>>> math.sin(2 * math.pi)
-2.4492935982947064e-16
>>> math.sin(math.pi)
1.2246467991473532e-16

问题是,第一个结果是如何以 -2.0 结束的,我可能希望它 return 接近于零或错误/nan?

如果您有兴趣知道,这个等式应该给出一条线的长度 (x),该线在一个点和另一个点之间延伸 2 点,其形状具有 n 个等于 1 的边并且所有角都相等(边长为 1 的等边形的最小对角线的长度)。

这些错误来自简单的浮点运算...这就是它的工作方式。所以,你可以避免使用浮点数,但这可能会给你带来很多痛苦。

如果您了解浮点数的工作原理 here,您会发现错误会发生……很多。这基本上就是为什么大多数数据库支持定点精度进行货币计算的原因。也就是说,sin/cos 在定点上会很痛苦...

避免这些错误的一个简单方法是对 sin/cos.

的所有(或部分)180' 角使用简单的预先计算的查找 table

对于第二个结果,影响绝对值很小:1.22464679915e-16 是一个非常小的数字,非常接近于零,大约为 0.00000000000000012246

如果您只是想避免这个小错误对结果的显示方式产生重大影响,请使用格式化字符串,例如:

for i in range(1, 11):
    print "{0:.10f}".format(math.sin(2 * math.pi / i) / math.sin(math.pi / i))

输出:

-2.0000000000
0.0000000000
1.0000000000
1.4142135624
1.6180339887
1.7320508076
1.8019377358
1.8477590650
1.8793852416
1.9021130326

how did the first result end up with -2.0, I might expect it to return something close to zero or an error / nan?

第一个结果是i为1的时候,所以简化为:

math.sin(2 * math.pi) / math.sin(math.pi)

由于浮点错误,math.sin(2 * math.pi) 的计算结果为 -2.44921270764e-16,而 math.sin(math.pi) 的计算结果为 1.22460635382e-16

-2.44921270764e-16 / 1.22460635382e-16 的浮点除法得到 -2.0,这就是最终输出。

在没有浮点错误的情况下,math.sin(2 * math.pi)math.sin(math.pi) 都应该评估为零,但浮点实现和 [=46= 的组合怪癖恰恰发生了这种情况] sin 函数实现,第一个的浮点误差是第二个的两倍,并且是负数,所以将它们相除得到 -2.

math.pi * 2 的内部表示中的浮点误差是 math.pi 的两倍,因为当你将带有误差范围的值加倍时,误差范围也会加倍,并且不可能将 pi 表示为没有错误的浮点数。推测:该错误通过 sin 计算传播,并且由于 sin 计算中与 pi 到 2 pi 范围内的 sin 函数是 0 到圆周率

如果要将极小的值四舍五入为零,可以使用numpy.around四舍五入到给定的小数位数,例如:

import numpy
for i in range(1, 11):
    print numpy.around(numpy.sin(2 * numpy.pi / i), 15) / numpy.around(numpy.sin(numpy.pi / i), 15)

这会为第一个结果生成一个 nan。