在用Javascript和Canvas编写的simple geometric program中,当我将角度设置为270°(1½π)时,我预计Math.cos(θ)将变为零。矢量从中心向下直线,笛卡尔网格上没有x距离。相反,我得到了这个:
demo_angle = 270
ang = demo_angle * Math.PI / 180
x = Math.cos(ang)
console.log(x)
> -1.836909530733566e-16
要查看数学函数的输出,请查看控制台。源代码在URL中可见一级(在coffeescript中)。
我必须在我的代码中定义“绝对值小于1e-15的任何数字应该被认为是零”,但这真的不令人满意。毋庸置疑,当我尝试使用x
这么小的值进行数学运算时,特别是因为我试图在x
中使用{{1}}作为斜率计算的分母然后进行一些二次操作,我最终会来数字超过Number.MAX_VALUE(或Number.MIN_VALUE)。
我知道浮点数学在汇编语言层面上是一种黑暗艺术,但是这样的结果似乎比可接受的更奇怪。关于我应该做什么的任何提示?
答案 0 :(得分:9)
问题不在于“零不完全为零”。相反,零正好为零。
您遇到的问题是3π/ 2不能表示为浮点数。所以你实际上取的值的余弦不等于3π/ 2。这种表示错误有多大?大约1.8e-16,这是您在余弦中看到的错误的来源。
有些语言通过提供像sinpi
和cospi
这样的函数来解决这个问题,这些函数隐式地将它们的参数缩放π因子;这是提供精确结果的一种方法。显然,这不是你的选择,因为javascript没有这样的功能。如果你愿意,你可以自己动手,利用这些功能的对称性,或者你可以简单地将“接近零”的值钳制到零,就像你现在一样。两者都不是特别令人满意,但两者都可能适用于您的目的。
答案 1 :(得分:3)
问题在于Math.PI不完全等于Pi,而是形式为m * 2 ^ e的数字,其中2 ^ 52 <= m <1。最接近它的2 ^ 53。
然后乘以270会引入一个小的舍入误差。
然后除以180会导致更多的舍入误差。
所以你的ang
值并不完全等于3 * Pi / 2,因此,你得到的不是你期望的0。
计算本身实际上非常准确。