此问题不是由long double转换为double导致的。这可能是由于数学库中的sin
例程不准确。
fsin
指令被指定为在其范围内的操作数产生1 ULP(长双倍格式)内的结果(根据Intel 64和IA-32架构软件开发人员手册,2011年10月,第1卷,8.3。10),以圆整模式。在提问者值为-5.07121364272633190495298549649305641651153564453125或-0x1.448ec3aaa278dp + 2的Intel Core i7,fsin
上产生0xe.fb206c69b0ba402p-4。我们可以很容易地从这个十六进制数中看到最后11位是100 0000 0010.这些是从long double转换时将被舍入的位。如果它们大于10000000000,则数字将被四舍五入。他们更大。因此,将此long double值转换为double的结果是0xe.fb206c69b0ba8p-4,它等于0x1.df640d8d36175p-1和0.93631021832247418590355891865328885614871978759765625。还要注意的是,即使结果是一个ULP较低,最后的11位仍然会大于10000000000,并且仍然会四舍五入。因此,在符合上述文档的Intel CPU上,此结果不应改变。
比较此直接计算双精度正弦,使用理想的sin
例程,产生正确的舍入结果。值的正弦值大约是0.93631021832247413051857150785044253634581268961333520518023697738674775240815140702992025520721336793516756640679315765619707343171517531053811196321335899848286682535203710849065933755262347468763562(用枫10计算)。与此最接近的是0x1.df640d8d36175p-1。这与我们通过将fsin
结果转换为两倍而获得的值相同。
因此,该差异不是由长双倍转换为双倍所引起的;将长双倍结果转换为双倍结果产生与理想双精度例程完全相同的结果。
我们没有关于提问者的Visual Studio包使用的sin
例程的准确性的规范。在商业图书馆中,允许1 ULP或几个ULP的错误是常见的。观察正弦到双精度值四舍五入的点的距离:从双精度值开始,它是.498864 ULP(双精度ULP),因此它距离舍入更改的点为0.001136 ULP。因此,即使sin
例程中的一个非常微小的不准确性,也会导致它返回0x1.df640d8d36174p-1而不是更接近0x1.df640d8d36175p-1。
因此,我猜想这种差异的根源在sin
例程中是一个非常小的不准确性。
一个操作可以完全在浮点寄存器中进行。当80位寄存器写入64位存储器地址时,另一个会导致精度损失。 FSTP文档说,“将值存储在内存中时,该值将转换为单实或双实格式。” –
'fsin'方法使用80位精度的x87 FPU,在MSVC(我使用2010)中执行'sin'似乎使用SSE及其128位xmm *寄存器。 (另请参阅[此问题](http://stackoverflow.com/questions/2284860/how-does-c-compute-sin-and-other-math-functions)。) – DCoder