[英]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
后 ,将进行从int
到double
的转换。
编译器不会根据您决定存储结果的类型为您选择double
算术。编译器将根据操作数的类型选择最佳的运算符重载,在这种情况下,这些操作数均为int
并寻找有效的隐式转换(在这种情况下int
到double
) 之后 ; 因此,它正在选择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
精度运算。
另外,值得指出的是,您的构造函数逻辑还存在其他一些问题:
您应该验证输入数据; 指定负分钟或秒数是否有效? IMO似乎不合理。 应仅将deg
设置为负值。 您应该检查这种情况并采取相应的措施:抛出一个异常(最好),或者根据deg
符号对min
和sec
的符号进行归一化(难看并可能引起混淆)。
您的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;
}
他们使用非常大的负数和正数来确保归一化将角度正确限制在[-180,180]度范围内。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.