这个问题不是由 long double 到 double 的转换引起的。这可能是由于sin
数学库中的例程不准确。
该fsin
指令指定为在其范围内的操作数(根据英特尔 64 和 IA-32 架构软件开发人员手册,2011 年 10 月,第 1 卷,8.3.10)在 1 ULP(长双精度格式)内产生结果到最近模式。在英特尔酷睿 i7 上,fsin
提问者的值,-5.07121364272633190495298549649305641651153564453125 或 -0x1.448ec3aaa278dp+2,产生 0xe.fb206c69b0ba402p-4。我们可以很容易地从这个十六进制看到最后 11 位是 100 0000 0010。这些是从 long double 转换时将被舍入的位。如果它们大于 100 0000 0000,则数字将向上取整。他们更大。因此,将这个 long double 值转换为 double 的结果是 0xe.fb206c69b0ba8p-4,等于 0x1.df640d8d36175p-1 和 0.93631021832247418590355891865328885614871978759765625。另请注意,即使结果低一个 ULP,最后 11 位仍将大于 100 0000 0000 并且仍会向上取整。因此,此结果在符合上述文档的 Intel CPU 上应该不会有所不同。
sin
将此与使用产生正确舍入结果的理想例程直接计算双精度正弦进行比较。的值的正弦近似0.93631021832247413051857150785044253634581268961333520518023697738674775240815140702992025520721336793516756640679315765619707343171517531053811196321335899848286682535203710849065933755262347468763562(的Maple 10计算的)。最接近此的双精度为 0x1.df640d8d36175p-1。这与我们通过将fsin
结果转换为 double 获得的值相同。
因此,差异不是由 long double 转换为 double 引起的;将 long double结果转换为 double 会产生与理想双精度例程fsin
完全相同的结果。sin
sin
对于提问者的 Visual Studio 包所使用的例程的准确性,我们没有规范。在商业图书馆中,允许 1 个 ULP 或多个 ULP 的错误是很常见的。观察正弦与双精度值舍入的点有多接近:距双精度值的距离为 0.498864 ULP(双精度 ULP),因此距舍入更改的点为 0.001136 ULP。因此,即使例程中非常轻微的不准确sin
也会导致它返回 0x1.df640d8d36174p-1 而不是更接近的 0x1.df640d8d36175p-1。
因此,我推测差异的来源是sin
例程中的一个非常小的不准确。