角度标准化C#

时间:2014-10-11 19:15:29

标签: c# math angle pi

我有一个具有此构造函数的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作为这种格式?

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

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

4 个答案:

答案 0 :(得分:1)

问题在于这段代码:

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

虽然您将seconds存储为double,但在 int之后,doublesec + 60 * (min + 60 * deg)的转换正在进行计算int

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

Int32.MinValue / 60 * 60 * 60 = Int32.MinValue * 60&lt; Int32.MinValue会溢出。

120 + 180 * 200000 * 60 * 60&gt; Int32.MaxValue也将溢出。

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

要解决此问题,请将代码更改为:

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

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

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

  1. 您应该验证输入数据;是否有效指定负分钟或秒? IMO似乎不合理。只应允许deg具有负值。您应检查此情况并采取相应措施:根据min的标志(丑陋且可能令人困惑)抛出异常(优先)或标准化secdeg的标志。

  2. 您的seconds计算对于负角度似乎不正确(同样,这与前一个问题以及您决定实施的任何符号约定相关联)。除非惯例是负角度必须为负degminsec,否则计算seconds的方式是错误的,因为您总是添加分钟和秒数条款无论是deg

  3. 的标志

    更新在我有机会测试之前,您的代码中还有一个问题。您的某些测试用例失败,因为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;
     }
    

答案 1 :(得分:0)

    //  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;
}     

这是我的标准化功能

答案 2 :(得分:0)

Dim ValArray As Variant Set WB2 = GetObject(FileName2) 'Get the Destination Excel File Handle which is already opened WB2.Worksheets("WebADI").Unprotect Set Myexcel = CreateObject("excel.application") 'Create Excel Object Myexcel.Visible = True Set WB1 = Myexcel.Workbooks.Open(Filename) 'Open the Source Excel File ValArray = WB1.Worksheets("CHPGEFABRFQ").Range("$B$5:$AE$11").Value WB2.Worksheets("WebADI").Range("$C$5").Resize(UBound(ValArray), UBound(ValArray, 2)).Value = ValArray 循环通常是个坏主意。如果你处理小值也没关系,但想象一下你有一个像 1e+25 这样的角度,如果你有一个像样的 CPU,那就是 1.59e+24 次迭代或大约 1 亿年的计算时间。 应该如何做:

while

答案 3 :(得分:-1)

他们使用非常大的负数和正数来确保标准化上限正确地适应范围[-180,180]度。