Python 各种角度的精确正弦/余弦/切线

Python 各种角度的精确正弦/余弦/切线,python,math,Python,Math,有没有办法得到一个角度的精确切线/余弦/正弦(弧度) math.tan() >>> from math import * >>> from decimal import Decimal >>> sin(pi) # should be 0 1.2246467991473532e-16 >>> sin(2*pi) # should be 0 -2.4492935982947064e-16 >>> cos(pi/

有没有办法得到一个角度的精确切线/余弦/正弦(弧度)

math.tan()

>>> from math import *
>>> from decimal import Decimal
>>> sin(pi) # should be 0
1.2246467991473532e-16
>>> sin(2*pi) # should be 0
-2.4492935982947064e-16
>>> cos(pi/2) # should be 0
6.123233995736766e-17
>>> cos(3*pi/2) # 0
-1.8369701987210297e-16
>>> tan(pi/2) # invalid; tan(pi/2) is undefined
1.633123935319537e+16
>>> tan(3*pi/2) # also undefined
5443746451065123.0
>>> tan(2*pi) # 0
-2.4492935982947064e-16
>>> tan(pi) # 0
-1.2246467991473532e-16
我尝试使用Decimal(),但这也没有帮助:

>>> tan(Decimal(pi)*2)
-2.4492935982947064e-16
numpy.sin(x)
和其他三角函数也有同样的问题

或者,我也可以创建一个带有值字典的新函数,例如:

def new_sin(x):
    sin_values = {math.pi: 0, 2*math.pi: 0}
    return sin_values[x] if x in sin_values.keys() else math.sin(x)

然而,这似乎是一种廉价的解决方法。还有别的办法吗?谢谢

不可能在计算机中存储pi的精确数值
math.pi
是可存储在Python浮点中的与pi最接近的近似值
math.sin(math.pi)
返回近似输入的正确结果

为了避免这种情况,您需要使用支持符号算术的库。例如,对于sympy:

>>> from sympy import *
>>> sin(pi)
0
>>> pi
pi
>>> 

sympy将对表示pi的对象进行操作,并可以给出精确的结果。

当您处理不精确的数字时,需要明确地处理错误
math.pi
(或
numpy.pi
)并不完全是
π
,例如,它是56位中距离
π
最近的二进制有理数。并且该数字的
sin
不是
0

但它非常接近于0。同样地,
tan(pi/2)
不是无穷大,而是巨大的,
asin(1)/pi
非常接近0.5

因此,即使算法是精确的,结果也不会精确

如果你从未读过书,现在就应该读

处理这个问题的方法是使用ε比较,而不是在任何地方都使用精确比较,并在打印时显式地取整,等等

使用数字而不是浮动
numbers使这更容易。首先,您可能使用十进制而不是二进制进行思考,因此更容易理解错误并做出决策。其次,您可以显式地设置
Decimal
值的精度和其他上下文信息,而
float
始终是IEEE双精度值


正确的方法是对算法进行全面的错误分析,适当地传播错误,并在需要的地方使用这些信息。简单的方法是选择一些对应用程序“足够好”的显式绝对或相对ε(以及无穷大的等价物),并在任何地方使用它。(您可能还希望使用适当的领域特定知识,将某些值视为pi的倍数,而不仅仅是原始值。)

当使用浮点数的有限长度表示法时,无法准确表示无理数。trig函数的结果通常是非理性的,因此无法在数字计算机上精确地表示它们。您看到的是浮点数的限制,如果您的问题的值与0的距离不够近,您可能需要重新考虑如何解决它。更重要的是,
math.pi
并不完全是
pi
,因此即使
sin
是精确的,它仍然不是
0
。这是由于浮点计算的限制。有几种方法可以解决这个问题。符号计算,适当调整精度,等等。如果你说你想要它做什么,可能会有一个解决方案。这是浮点的本质,你无法回避它。即使是dict查找也只适用于某些情况。例如(52*math.pi)/52!=对于大多数用例来说,math.piThis是一个很好的解决方案。如果你需要近似的数值,你可以得到它,但尽量避免这样做。这可能就是你所说的巫术,但这不足以解决你的问题吗?:从数学导入numpy作为np导入pi def sin(x):如果x/pi==int(x/pi):返回0 elif x/pi!=int(x/pi):返回np.sin(x)@DavidM.Sousa不是一个好的解决方案,因为
int((11*math.pi)/math.pi)
是10。@casevh在这种情况下,它是10这一事实是不相关的,因为10或11都会给出0。int((11*math.pi)/math.pi)实际上是int(10.999999999),自然是10。