-1

我使用 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;
}
4

1 回答 1

0

大多数语言和编译器对 IEEE 浮点标准的支持很差。这是因为每个操作员调用都有许多模式和标志,以及 INF、NAN 甚至 0 的变体,并且这些语言并不便于程序员设置、检查或响应这些。

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

摘自IEEE 标准 754 二进制浮点算术状态的讲义,其主要架构师/发起者(大约在 1997 年,介于 1984 年的原始版本和 2008 年的当前版本之间:

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

也许与此相关的是:

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

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

于 2014-12-25T03:02:13.390 回答