【问题标题】:Conversion from degrees to radians not accurate enough从度数到弧度的转换不够准确
【发布时间】:2017-08-23 09:23:52
【问题描述】:

我很清楚从度数到弧度的转换是如何工作的,但我仍然有一个问题。问题在于它的准确性。

您可能知道System.Math 库中的三角函数使用弧度,但我需要三角函数,您可以在其中插入度数。所以我用CosSinTan 函数创建了一个Helper 类,它只会将它们内部的度数转换为弧度并调用它们的Math 函数。

public static class Helper
{
    public static double Sin(double degrees) => Math.Sin(degrees * Math.PI / 180.0);
    public static double Cos(double degrees) => Math.Cos(degrees * Math.PI / 180.0);
    public static double Tan(double degrees) => Math.Tan(degrees * Math.PI / 180.0);
}

但是,例如,当我使用 Math.Tan(45) 时,它返回 0.999999999989 而不是 1。我的朋友们,我的问题是如何做到这一点完全准确。

提前致谢。

编辑: 我明白,会有一些小错误,而且不可能摆脱它们。我真正想知道的是如何将值四舍五入所需的数量(不要太多,也不要太少),这样它仍然可以工作。

编辑2: 上下文在这个问题中非常重要,所以这里是:

我的Line 类需要三角函数,该类具有以下构造函数:

public Line(Vector2 beginning, Vector2 end)
{
    this.beginning = beginning;
    this.end = end;
    this.direction = 180.0 * Math.Atan(end.Y - beginning.Y / end.X - beginning.X) / Math.PI;
    this.slope = Helper.Tan(direction);
    this.length = Helper.DistanceBetweenTwoPoints(beginning, end);
    this.offset = beginning.X;
 }

问题出现在我为斜率赋值的那条线上,我需要使用线性代数检查一个点是否在这条线上。

披露:Vector2 是一个具有double xdouble y 的结构。就是这样。

【问题讨论】:

    标签: c# trigonometry


    【解决方案1】:

    在使用双精度时,您总是会遇到舍入错误。这就是浮点运算的本质。 Double 可以携带大约 16 个十进制数字。并且在每次操作中,错误都会累加。 “诀窍”是接受这个错误,永远不要将双精度/浮点数与相等性进行比较(除非您知道自己在做什么)并对输出进行有意义的舍入。

    http://floating-point-gui.de/

    要确定一个点是否位于两点之间的线上或类似问题,您不需要四舍五入,而是使用容差:计算点与线之间的最短距离并检查它是否低于阈值。这个阈值可以/应该有多大取决于您的应用程序,并且没有简单的答案。例如,如果您的分数在 0..10 的范围内,那么 1e-7 的容差对我来说是合理的。如果你想要一个通用的实现,你应该采取最大的

    • 点的距离乘以一个因子(例如dist(begin, end) * 1e-8
    • 非常小的距离的绝对值(例如1e-10

    但是您仍然会遇到问题。例如,如果您的线路从(10000000.1,0)(10000000.2,0),请参阅here 了解详细信息。总的来说,这是一个广泛的话题。

    【讨论】:

    • 我知道总会有错误,我也知道为什么。也许我问错了问题
    • @huB1erTi2:稍微扩展了答案。这是你的意思吗?
    • 嗯,我需要 total 准确度,但我知道这是不可能的,所以我真正需要的是一种方便的方法来舍入该值,但不要太多。跨度>
    • 对于 total 的准确性,请查看 GNU MPL。四舍五入:距离地球太阳的误差约为 1.5m。您可以舍入多少取决于您的申请,但通常 6-8 位就足够了。
    • 我觉得最好给你上下文,等一下
    【解决方案2】:

    使用通过Helper.Tan(direction) 获得的斜率来计算一个点是否在一条线上,对于不同的斜率会产生不同数量的误差,随着方向接近+/- PI / 2 而变得更糟,因为tan(x) 接近无穷大当x 接近+/- PI/2+/- PI/2 它根本不起作用。

    要查看一个点是否在一条线附近,请使用该线的矢量与该点的矢量的叉积。例如,线是从点AB,点是P,那么当P 在线时,Cross(B-A,P-A) 将为零。为了解决与线长相关的误差,将叉积除以线长的平方,并与任意小公差epsilon 进行比较。

    // A,B, P are Vector2
    double cross = (B.x-A.x) * (P.y-A.y) - (B.y-A.y) * (P.x-A.x);
    cross /= (B.x-A.x) * (B.x-A.x) + (B.y-A.y) * (B.y-A.y);
    double epsilon = 1.0e-6; // 1 millionths of a unit
    if (Math.abs(cross) < epsilon) {
         // point P is on line A,B
    } 
    

    顺便说一句,Math.PI 是一个无理数的近似值,无论您使用什么系统,使用它总是会引入错误。 PI 根本不能写成数字。在现实世界中,所有测量都有误差,对于 PI 7 位 3.141592 的所有实际应用来说,精度已经绰绰有余。

    【讨论】:

    • 优秀的答案。为了避免 PI 的固有问题,只需将数字作为度数存储在内存/持久内存中,并根据需要进行转换。人们似乎忘记了为什么发明度数......有一种简单的方法将直角除以 2、3、6、10 等。为此,它们非常出色。如果用户输入度数,则很容易四舍五入到最接近的 100 度或 1000 度。对于 PI,最好存储一个 PI 的一部分。
    猜你喜欢
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 2015-05-24
    • 1970-01-01
    相关资源
    最近更新 更多