仅在dot42项目中返回NaN的数学算法

时间:2014-12-24 23:01:14

标签: c# nan ieee-754 dot42

我使用dot42创建了一个示例项目,并使用了这个C#代码并将其放在库中:

var gcd = GetGreatCircleDistanceKm(52.0, -1.90,  21.0, 39.0); // returns NaN should be ~4915

public double GetGreatCircleDistanceKm(double startLat, double startLong, double endLat, double endLong)
{
    var earthRadius = Constants.EarthRadiusKm;
    var φ1 = DegreesToRadians(startLat);
    var φ2 = DegreesToRadians(endLat);
    var Δφ = DegreesToRadians(endLat - startLat);
    var Δλ = DegreesToRadians(endLong - startLong);
    var a = Math.Sin(Δφ / 2) * Math.Sin(Δφ / 2) + Math.Cos(φ1) * Math.Cos(φ2) * Math.Sin(Δλ / 2) * Math.Sin(Δλ / 2);
    var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
    return earthRadius * c;
}

public static double DegreesToRadians(double angle)
{
    return Math.PI * angle / 180.0;
}
然而,

问题是c始终返回NaN,因此函数返回NaN。这种方法在普通的.net项目中运行良好。

我尝试了同一个haversine公式的许多版本,认为我可能输入了错误的内容,但不是,都返回NaN

公式来源:http://www.movable-type.co.uk/scripts/latlong.html

解决方案(奇怪的是):

var a1 = Δφ/2;
var a = Math.Sin(a1) * Math.Sin(a1) + Math.Cos(φ1) * Math.Cos(φ2) * Math.Sin(Δλ / 2) * Math.Sin(Δλ / 2);

不知道它是如何工作的,但它确实有效。如果有人可以放光,请。

完整修正版

/// <summary>
/// Gets the shortest possible distance between two points on earth
/// </summary>
/// <param name="startLat"></param>
/// <param name="startLong"></param>
/// <param name="endLat"></param>
/// <param name="endLong"></param>
/// <returns></returns>
public double GetGreatCircleDistanceKm(double startLat, double startLong, double endLat, double endLong)
{
    var startLatRad = DegreesToRadians(startLat);
    var endLatRad = DegreesToRadians(endLat);
    var latDiffRad = DegreesToRadians(endLat - startLat);
    var longDiffRad = DegreesToRadians(endLong - startLong);
    var halfLatDiff = latDiffRad/2;
    var a = Math.Sin(halfLatDiff) * Math.Sin(halfLatDiff) + Math.Cos(startLatRad) * Math.Cos(endLatRad) * Math.Sin(longDiffRad / 2) * Math.Sin(longDiffRad / 2);
    var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
    return Constants.EarthRadiusKm * c;
}

1 个答案:

答案 0 :(得分:0)

大多数语言和编译器都很难支持IEEE浮点标准。这是因为每个操作员调用有很多模式和标志,INF,NAN甚至0都有变化,语言不容易让程序员设置,检查或响应这些。

例如:如果这里有一个Math.Sin除以2下溢(太小而无法表示)或导致精度丢失(因为最接近0的结果不准确到完整的位数)那么这可以设置标志或返回值(也取决于模式);如果它位于表达式的中间,就像您对变量a的原始赋值那样,那么语言/编译器只能处理一个案例,这可能不是您需要的案例。鉴于用于分配给halfLatDiff等中间变量的语言/编译器规则,取决于结果值,右手表达式的标志和模式,并且可能包括对赋值的一些更改,可能恰好给出了不同的答案。

来自Lecture Notes on the Status of IEEE Standard 754 for Binary Floating-Point Arithmetic由其主要建筑师/发起人(大约1997年的原始1984年和当前的2008版本之间:

  

这些功能缺乏编程语言和编译器的支持,所以   这些功能处理不当和/或实际上无法使用,所以那些   功能鲜为人知,需求较少,所以这些功能   缺乏编程语言和编译器的支持。

这里可能相关的是:

  

当用于评估 b±c d时,融合MAC会产生异常

(如果您阅读了程序的汇编代码(即使您只知道汇编代码的基础知识,这可能已经足够清晰),您也可以看到表达式的解释方式。但是很多编译器都没有正确解释考虑到他们的硬件的缓存和流水线。你的调试器也没有。所以你仍然可能无法重建观察到的输出的原因。)