我有一个具有此构造函数的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;
}
答案 0 :(得分:1)
问题在于这段代码:
double seconds = sec + 60 * (min + 60 * deg);
虽然您将seconds
存储为double
,但在 int
之后,double
到sec + 60 * (min + 60 * deg)
的转换正在进行计算为int
。
编译器不会根据您决定存储结果的类型为您选择double
算术。编译器将根据操作数的类型选择最佳的运算符重载在这种情况下,所有int
都会查找有效的隐式转化(在这种情况下为int
到double
)之后;因此它选择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
算术。
此外,值得指出的是构造函数逻辑还有其他一些问题:
您应该验证输入数据;是否有效指定负分钟或秒? IMO似乎不合理。只应允许deg
具有负值。您应检查此情况并采取相应措施:根据min
的标志(丑陋且可能令人困惑)抛出异常(优先)或标准化sec
和deg
的标志。
您的seconds
计算对于负角度似乎不正确(同样,这与前一个问题以及您决定实施的任何符号约定相关联)。除非惯例是负角度必须为负deg
,min
和sec
,否则计算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;
}
答案 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]度。