我正在尝试一些我认为应该相当简单的事情。我有一个角度,一个位置和一个距离,我想从这些信息中找到X,Y坐标。
使用90度的示例输入,我使用以下代码将值转换为弧度:
public double DegreeToRadian(float angle)
{
return Math.PI * angle / 180.0;
}
这给了我1.5707963267949弧度然后当我使用
Math.Cos(radians)
我最终得到了一个答案:6.12303176911189E-17
到底他妈发生了什么? 90度的余弦应该是0,所以为什么我会得到这样的偏差...更重要的是我怎么能阻止它呢?
见上面的答案。请记住,6.12303176911189E-17是0.00000000000000006(我甚至可能错过了零!)所以这是一个非常非常小的偏差。
阅读floating point arithmetic。它永远也永远不会是精确的。永远不要与任何东西完全比较,但要检查数字是否因(小)epsilon而不同。
由于计算结果非常接近0(零),您可以使用舍入:
Math.Round(result, 4): // 4 decimals, e.g.: 2.1234
所以,从弧度计算sin / cos:
const double Deg = Math.PI / 180;
double sin = Math.Round(Math.Sin(yourRadianValue * Deg), 4);
double cos = Math.Round(Math.Cos(yourRadianValue * Deg), 4); // 0.0000...06 becomes 0
如果yourRadianValue = 90
,返回sin = 1
和cos = 0
。
其他帖子对于使用浮点实现的实际问题是正确的,这些实现返回小错误的结果。但是,如果浮点库实现保留了众所周知的函数的基本身份,那将是很好的:
Math.Sin(Math.PI)
应该等于0
,
Math.Cos(Math.PI)
应该等于-1
,
Math.Sin(Math.PI/2)
应该等于1
,
Math.Cos(Math.PI/2)
应该等于0
等。
您可以预期浮点库会尊重这些和其他trigonometric identities,无论其常量值中的小错误(例如Math.PI)。
您从Math.Cos(Math.PI/2)
获得一个小错误的事实表明该实现正在计算结果,而不是从表中提取它。更好地实现Math.Cos
和其他超越函数对于特定身份可能更准确。
我确信在C#的情况下,这种行为是预期的,因此Microsoft无法在不影响现有代码的情况下对其进行更改。如果获得特定三角标识的精确结果对您很重要,则可以使用一些检查已知输入的代码来包装本机浮点函数。
你应该使用四舍五入
var radians = Math.PI * degres / 180.0;
var cos = Math.Round(Math.Cos(radians), 2);
var sin = Math.Round(Math.Sin(radians), 2);
结果将是:sin:1 cos:0
正如@ b1tw153所注意到的那样,如果为PI / 2的倍数返回了精确值,那就太好了。而这正是微软在他们的System.Numerics
库中所做的事情;如果你检查Matrix3x2.CreateRotation
的源代码,你会注意到它们手动处理n * PI / 2个案例:https://github.com/Microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Matrix3x2.cs#L325