角度归一化C#

本文关键字: | 更新日期: 2023-09-27 18:26:21

我有一个Angle类,它有这个构造函数

public  Angle(int deg, //  Degrees, minutes, seconds
                  int min, //    (Signs should agree
                  int sec) //       for conventional notation.)
{
   /* //Bug degree normalization 
    while (deg <= -180) deg += 360;
    while (deg > Math.PI) deg -= 360;
    //correction end */
    double seconds = sec + 60 * (min + 60 * deg);
    value = seconds * Math.PI / 648000.0;
    normalize(); 
}

我有这些值来测试构造函数

  int[] degrees = { 0, 180, -180, Int32.MinValue / 60, 120+180*200000};
        int[] minutes = { 0, 0, 0, 0,56};
        int[] seconds = { 0, 0, 0, 0,10};
        Console.WriteLine("Testing constructor Angle(int deg, int min)");
        for (int i = 0; i < degrees.Length; i++)
        {
            p = new Angle(degrees[i], minutes[i], seconds[i]);
            Console.WriteLine("p = " + p);
        }
        /*Testing constructor Angle(int deg, int min)
        p = 0°0'0"
        p = 180°0'0"
        p = 180°0'0"
        p = 0°8'0" incorrect output 
        p = -73°11'50"  incorrect output expected  120 56 10 
         */

我不明白为什么这里有虫子?为什么他们使用Int32.MinValue除以60和120+180*200000作为这种格式?

构造函数中的注释是对代码的更正

UPDATE:添加了normalize() 的代码

//  For compatibility with the math libraries and other software
//  range is (-pi,pi] not [0,2pi), enforced by the following function:  
void normalize()
{
    double twoPi = Math.PI + Math.PI;          
    while (value <= -Math.PI) value += twoPi;
    while (value >   Math.PI) value -= twoPi;
}  

角度归一化C#

问题出在这段代码中:

double seconds = sec + 60 * (min + 60 * deg);

尽管您将seconds存储为double,但在sec + 60 * (min + 60 * deg)被计算为int之后,从intdouble的转换正在进行。

编译器不会根据您决定存储结果的类型为您选择double算法。编译器将根据操作数的类型选择最佳运算符重载,在这种情况下,这些操作数都是int,然后查找有效的隐式转换(在这种情况中,intdouble;因此,它选择了int算法,并且在最后两个测试用例中运算会溢出:

CCD_ 13=CCD_;CCD_ 15将溢出。

CCD_ 16>CCD_。

您对这两种情况的预期结果可能没有考虑到这种行为。

为了解决这个问题,请将您的代码更改为:

double seconds = sec + 60 * (min + 60f * deg);

60显式设置为double类型的文字常量(60f)将强制编译器将所有操作解析为double算法。

此外,值得指出的是,您的构造函数逻辑还有一些其他问题:

  1. 您应该验证输入数据;指定负分钟或负秒是否有效?国际海事组织认为这似乎不合理。应该只允许deg具有负值。您应该检查此条件并采取相应的行动:抛出异常(首选)或基于deg的符号规范化minsec的符号(丑陋且可能令人困惑)。

  2. 对于负角度,您的seconds计算似乎不正确(同样,这与之前的问题以及您决定实施的任何符号约定有关)。除非约定负角度必须具有负的degminsec,否则计算seconds的方法是错误的,因为无论deg的符号如何,您总是在添加分和秒项。

UPDATE您的代码中还有一个问题,我在有机会测试它之前错过了。您的一些测试用例失败了,因为double没有足够的解决方案。我认为您的代码需要进行一些重大的重构;应首先调用normalize()。通过这种方式,您将始终管理严格限制的值,这些值不会导致溢出或精度损失。

我会这样做:

 public Angle(int deg, int min, int sec)
 {
     //Omitting input values check.
     double seconds = sec + 60 * (min + 60 * normalize(deg));
     value = seconds * Math.PI / 648000f;
 }
 private int normalize(int deg)
 {
     int normalizedDeg = deg % 360;
     if (normalizedDeg <= -180)
         normalizedDeg += 360;
     else if (normalizedDeg > 180)
         normalizedDeg -= 360;
     return normalizedDeg;
 }
    //  For compatibility with the math libraries and other software
//  range is (-pi,pi] not [0,2pi), enforced by the following function:  
void normalize()
{double twoPi = Math.PI + Math.PI;          
    while (value <= -Math.PI) value += twoPi;
    while (value >   Math.PI) value -= twoPi;
}     

这是我有的归一化函数

while循环通常是个坏主意。如果你处理小值,这没关系,但想象一下,你有一个像1e+25这样的角度,如果你有一台不错的CPU,那将是1.59e+24次迭代,或者大约1亿年的计算时间。应该如何做:

static double NormalizeDegree360(double value)
{
    var result = value % 360.0;
    return result > 0 ? result : result + 360;
}
static double NormalizeDegree180(double value)
{
    return (((value + 180) % 360) + 360) % 360 - 180;
}
static double TwoPI = 2*System.Math.PI;
static double NormalizeRadians2Pi(double value)
{
    var result = value % TwoPI;
    return result > 0 ? result : result + TwoPI;
}
static double NormalizeRadiansPi(double value)
{
    return (((value + System.Math.PI) % TwoPI) + TwoPI) % TwoPI - System.Math.PI;
}

他们使用非常大的负数和正数来确保标准化将角度正确地限制在[-180180]度的范围内。

相关文章:
  • 没有找到相关文章