Smi*_*Smi 8 c c++ assembly visual-c++ x87
请考虑以下代码:
// Filename fputest.cpp
#include <cmath>
#include <cstdio>
int main()
{
double x;
*(__int64 *) &x = 0xc01448ec3aaa278di64; // -5.0712136427263319
double sine1 = sin(x);
printf("%016llX\n", sine1);
double sine2;
__asm {
fld x
fsin
fstp sine2
}
printf("%016llX\n", sine2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用Visual C++ 2012(cl fputest.cpp)编译并执行程序时,输出如下:
3FEDF640D8D36174
3FEDF640D8D36175
Run Code Online (Sandbox Code Playgroud)
问题:
此问题不是由long double转换为double引起的.这可能是由于sin数学库中的例程不准确.
该fsin指令被指定,以产生1 ULP内的结果(在长双格式),用于其范围(每英特尔64和IA-32体系结构软件开发者手册,2011年10月,第1卷,8.3.10)中,廿四内的操作数到最近的模式.在英特尔酷睿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.936310218322474130518571507850442536345812689613335205125234977386747752408151407029920255207213367935161766406793157656197073431715175310538111963213358998482866862347468763562(用Maple 10计算).最接近它的双精度是0x1.df640d8d36175p-1.这是我们通过将fsin结果转换为double 获得的相同值.
因此,差异不是由长双转换引起的; 将long double fsin结果转换为double会产生与理想的双精度sin例程完全相同的结果.
我们没有sin关于提问者的Visual Studio包使用的例程的准确性的规范.在商业图书馆中,允许1个ULP或几个ULP的错误是常见的.观察正弦与双精度值舍入的点的接近程度:距离双精度为.498864 ULP(双精度ULP),因此距离舍入变化的点为.001136 ULP.因此,即使程序中的非常小的不准确sin也会导致它返回0x1.df640d8d36174p-1而不是更接近0x1.df640d8d36175p-1.
因此,我猜想差异的来源是sin例行程序中的一个非常小的不准确性.
| 归档时间: |
|
| 查看次数: |
1394 次 |
| 最近记录: |