【发布时间】:2017-08-05 04:46:37
【问题描述】:
函数sinpi(x) 计算sin(πx),函数cospi(x) 计算cos(πx),其中与π 的乘法隐含在函数内部。这些函数最初是由 Sun Microsystems 在late 1980s 中作为扩展引入到 C 标准数学库中的。 IEEE Std 754™-2008 在第 9 节中指定了等效函数 sinPi 和 cosPi。
有许多计算自然发生 sin(πx) 和 cos(πx)。一个非常简单的例子是 Box-Muller 变换(GEP Box 和 Mervin E. Muller,“A Note on the Generation of Random Normal Deviates”。数理统计年鉴,第 29 卷,第 2 期。 2, pp. 610 - 611),给定两个具有均匀分布的独立随机变量 U1 和 U2,产生具有标准正态分布的独立随机变量 Z1 和 Z2:
Z₁ = √(-2 ln U₁) cos (2 π U₂)
Z₂ = √(-2 ln U₁) sin (2 π U₂)
另一个例子是计算度数参数的正弦和余弦,如使用Haversine公式计算大圆距离:
/* This function computes the great-circle distance of two points on earth
using the Haversine formula, assuming spherical shape of the planet. A
well-known numerical issue with the formula is reduced accuracy in the
case of near antipodal points.
lat1, lon1 latitude and longitude of first point, in degrees [-90,+90]
lat2, lon2 latitude and longitude of second point, in degrees [-180,+180]
radius radius of the earth in user-defined units, e.g. 6378.2 km or
3963.2 miles
returns: distance of the two points, in the same units as radius
Reference: http://en.wikipedia.org/wiki/Great-circle_distance
*/
double haversine (double lat1, double lon1, double lat2, double lon2, double radius)
{
double dlat, dlon, c1, c2, d1, d2, a, c, t;
c1 = cospi (lat1 / 180.0);
c2 = cospi (lat2 / 180.0);
dlat = lat2 - lat1;
dlon = lon2 - lon1;
d1 = sinpi (dlat / 360.0);
d2 = sinpi (dlon / 360.0);
t = d2 * d2 * c1 * c2;
a = d1 * d1 + t;
c = 2.0 * asin (fmin (1.0, sqrt (a)));
return radius * c;
}
对于 C++,Boost 库提供 sin_pi 和
cos_pi,一些供应商提供sinpi 和cospi 功能作为系统库中的扩展。例如,Apple 在 iOS 7 和 OS X 10.9 中添加了__sinpi、__cospi 以及相应的单精度版本__sinpif、__cospif(presentation,幻灯片 101)。但是对于许多其他平台,没有 C 程序可以轻松访问的实现。
与使用例如的传统方法相比sin (M_PI * x) 和 cos (M_PI * x),使用 sinpi 和 cospi 通过与 π 的乘法 internal 减少舍入误差来提高准确性,并且由于更简单的参数减少,还提供了性能优势.
如何使用标准 C 数学库以合理高效且符合标准的方式实现 sinpi() 和 cospi() 功能?
【问题讨论】:
-
为了同时获得最大的准确性和便携性,在我看来,暂时将舍入模式(例如使用
fenv()或fesetround())更改为截断/舍入为零是必要的。这样我们就可以使用例如Kahan sum/compensated sum,并将高精度系数拆分为几个不同的有限精度因子。其他所有方法似乎都依赖于特定的硬件(例如fma(),其仿真速度非常慢)或实现细节。 -
@NominalAnimal 我没有针对最大可移植性,因为这不是我需要的。对于想要在自己的实现中解决这些问题的人,我在回答中指出了各种潜在的症结。至于 FMA,它可以作为最近(大约过去 5 年)x86 和 ARM 处理器的硬件指令使用,当然还有自 1990 年代以来的 Power[PC]。如果有人想提供针对 FMA-less 硬件平台优化的代码的答案,我很乐意支持它(如果它真的很好,还会给予额外的奖励)。
标签: c floating-point trigonometry math.h