c++ - Visual C++ 2012 中的正弦计算不一致?

标签 c++ c visual-c++ assembly 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;
}

当使用 Visual C++ 2012 (cl fputest.cpp) 编译并执行程序时,输出如下:

3FEDF640D8D36174
3FEDF640D8D36175

问题:

  • 为什么这两个值不同?
  • 是否可以发布一些编译器选项,以便计算出的正弦值完全相同?

最佳答案

这个问题不是long double转double引起的。这可能是由于数学库中的 sin 例程不准确。

fsin 指令被指定为其范围内的操作数生成 1 ULP(长 double 格式)内的结果(根据 Intel 64 和 IA-32 架构软件开发人员手册,2011 年 10 月,第 1 卷,8.3.10),采用四舍五入模式。在 Intel Core 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 上应该不会有所不同。

将此与直接计算 double 正弦进行比较,使用产生正确舍入结果的理想 sin 例程。 The sine of the value is approximately 0.93631021832247413051857150785044253634581268961333520518023697738674775240815140702992025520721336793516756640679315765619707343171517531053811196321335899848286682535203710849065933755262347468763562 (computed with Maple 10).最接近它的是 0x1.df640d8d36175p-1。这与我们通过将 fsin 结果转换为 double 获得的值相同。

因此,差异不是由 long double 转换为 double 造成的;将 long double fsin 结果转换为 double 产生的结果与理想的 double sin 例程完全相同。

我们没有关于提问者的 Visual Studio 包使用的 sin 例程的准确性的规范。在商业图书馆中,允许 1 个 ULP 或几个 ULP 的错误是很常见的。观察正弦与 double 值四舍五入点的接近程度:它与 double 值相距 .498864 ULP( double ULP),因此与四舍五入变化的点相距 .001136 ULP。因此,即使 sin 例程中出现非常轻微的错误,也会导致它返回 0x1.df640d8d36174p-1 而不是更接近的 0x1.df640d8d36175p-1。

因此,我推测差异的来源是 sin 例程中的一个非常小的错误。

关于c++ - Visual C++ 2012 中的正弦计算不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12225668/

相关文章:

c - 为什么父进程根本不执行?

c++ - CEdit::GetLine() Windows 7

c++ - 反汇编 Visual C++ 中我的函数及其参数的名称

windows - 有没有办法检测 Windows 操作系统是服务器 (2003/2008) 还是工作站 (XP/Vista/Win7)?

c - PETSC_VIEWER_DRAW_WORLD 什么都不显示

c++ - 如何检测文件是否被覆盖?,

c++ - 我可以在字符串的 sprintf 中使用 sizeof() 或 #define 来提高精度吗?

c++ - 为什么这在 C++ 中返回零?

c++ - 从字符串转换为double和int-C++

c - C 中的动态 3D 可变列字符数组操作