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