繁体   English   中英

角度归一化C#

[英]Angle Normalization C#

我有一个具有此构造函数的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 
         */

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

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

更新 :添加了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;
}  
    //  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;
}     

这是我的规范化功能

问题出在这段代码中:

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

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

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

Int32.MinValue / 60 * 60 * 60 = Int32.MinValue * 60 < Int32.MinValue将会溢出。

120 + 180 * 200000 * 60 * 60 > Int32.MaxValue也会溢出。

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

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

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

60显式设置为double精度型常量( 60f )将强制编译器将所有运算解析为double精度运算。

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

  1. 您应该验证输入数据; 指定负分钟或秒数是否有效? IMO似乎不合理。 应仅将deg设置为负值。 您应该检查这种情况并采取相应的措施:抛出一个异常(最好),或者根据deg符号对minsec的符号进行归一化(难看并可能引起混淆)。

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

更新在您有机会对其进行测试之前,我错过了代码中的另一个问题。 您的一些测试用例失败了,因为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;
 }

他们使用非常大的负数和正数来确保归一化将角度正确限制在[-180,180]度范围内。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM