如何处理浮点计算中的过度精度?

时间:2009-02-02 14:49:47

标签: c++ c floating-point

在我的数值模拟中,我的代码类似于以下代码段

double x;
do {
  x = /* some computation */;
} while (x <= 0.0);
/* some algorithm that requires x to be (precisely) larger than 0 */

对于某些平台上的某些编译器(例如gcc)(例如linux,x87 math),有可能以高于双精度(“具有过度精度”)的方式计算x。 (更新:当我在这里谈到精度时,我的意思是精度/和/范围。)在这种情况下,可以想象,即使下一次x,比较(x <= 0)也会返回false。向下舍入为双精度,它变为0.(并且无法保证x在任意时间点都不会向下舍入。)

有没有办法执行这种比较

  • 是便携式的,
  • 适用于内联的代码,
  • 没有性能影响和
  • 不排除某些任意范围(0,eps)?

我尝试使用(x < std::numeric_limits<double>::denorm_min()),但在使用SSE2数学时,这似乎显着减慢了循环速度。 (我知道非正规可以减慢计算速度,但我没想到它们只是移动并比较慢。)

更新的 另一种方法是在比较之前使用volatile强制x进入内存,例如通过写作

} while (*((volatile double*)&x) <= 0.0);

但是,根据应用程序和编译器应用的优化,此解决方案也会引入明显的开销。

更新的 任何容忍的问题在于它是非常随意的,即它取决于具体的应用或上下文。我更愿意在没有过多精度的情况下进行比较,这样我就不必做任何额外的假设或在我的库函数的文档中引入一些任意的epsil。

5 个答案:

答案 0 :(得分:7)

正如Arkadiy在评论中所述,明确的演员((double)x) <= 0.0 工作 - 至少根据标准。

C99:TC3,5.2.4.2.2§8:

  

除了赋值和强制转换(删除所有额外的范围和精度)之外,具有浮动操作数的操作值和通常算术转换以及浮动常量的值将被评估为范围和精度可能大于的格式。类型要求。 [...]


如果您在x86上使用GCC,则可以使用标志-mpc32-mpc64-mpc80将浮点运算的精度设置为single,double和extended double精度。

答案 1 :(得分:2)

在您的问题中,您声明使用volatile会起作用,但会有很大的性能影响。如何在比较期间使用volatile变量,允许x保存在寄存器中?

double x; /* might have excess precision */
volatile double x_dbl; /* guaranteed to be double precision */
do {
  x = /* some computation */;
  x_dbl = x;
} while (x_dbl <= 0.0);

您还应该检查是否可以通过显式使用long double加快与最小次正常值的比较并缓存此值,即

const long double dbl_denorm_min = static_cast<long double>(std::numeric_limits<double>::denorm_min());

然后比较

x < dbl_denorm_min

我认为一个体面的编译器会自动执行此操作,但是人们永远不会知道......

答案 2 :(得分:1)

我想知道你是否有正确的停止标准。听起来x&lt; = 0是异常条件,但不是终止条件,并且终止条件更容易满足。也许在你的while循环中应该有一个break语句,当满足一些容差时停止迭代。例如,当两个连续的迭代彼此足够接近时,许多算法终止。

答案 3 :(得分:0)

好吧,GCC有一个标志,-fexcess-precision会导致你正在讨论的问题。它还有一个标志,-ffloat-store,它解决了你正在讨论的问题。

“不要将浮点变量存储在寄存器中。这样可以防止诸如68000之类的机器出现不希望的超额精度,其中浮动寄存器(68881)的精度比双倍应该具有的精度更高。”

我怀疑该解决方案对性能没有影响,但影响可能不会过于昂贵。随机谷歌搜索表明它的成本约为20%。实际上,我不认为一个既可移植又不会影响性能的解决方案,因为强制芯片不使用过多的精度通常会涉及一些非自由操作。但是,这可能是您想要的解决方案。

答案 4 :(得分:0)

务必将检查设为绝对值。它需要是一个大约零,高于和低于的ε。

相关问题