请考虑以下代码:
0.1 + 0.2 == 0.3 -> false
Run Code Online (Sandbox Code Playgroud)
0.1 + 0.2 -> 0.30000000000000004
Run Code Online (Sandbox Code Playgroud)
为什么会出现这些不准确之处?
我目前正在研究进行多精度浮点运算的代码.为了正常工作,该代码要求在明确定义的点处将值降低到最终精度.因此,即使将中间结果计算到80位扩展精度浮点寄存器,在某些时候也必须将其四舍五入为后续操作的64位双精度.
代码使用宏INEXACT来描述此要求,但没有完美的定义.在GCC手册中提到-fexcess-precision=standard的办法迫使流延和赋值操作定义良好的精度.但是,它还写道:
对于C以外的语言,未实现"-fexcess-precision = standard"
现在我正在考虑将这些想法移植到C++(如果有人知道现有的实现,则欢迎评论).所以我似乎无法将该开关用于C++.但是没有任何开关的g ++默认行为是什么?是否有更多类似C++的方法来控制过度精度的处理?
我想对于我目前的用例,我可能会-mfpmath=sse在任何情况下使用,据我所知,这不应该产生任何过多的精度.但我仍然很好奇.
c++ floating-point gcc floating-point-precision extended-precision
我还没有创建一个程序来查看GCC是否需要它通过,当我这样做时,我想知道如何启用严格的浮点模式,这将允许运行和计算机之间的可重现结果,谢谢.
在我的数值模拟中,我的代码类似于以下代码段
double x;
do {
x = /* some computation */;
} while (x <= 0.0);
/* some algorithm that requires x to be (precisely) larger than 0 */
Run Code Online (Sandbox Code Playgroud)
对于某些平台上的某些编译器(例如gcc)(例如linux,x87 math),有可能x以高于双精度("具有过度精度")的方式计算.(更新:当我在这里谈到精度时,我的意思是精度/和/范围.)在这种情况下,可以想象,x <= 0即使下一次x向下舍入到双精度,它也会变为0,而compare()返回false.(和无法保证x不会在任意时间点向下舍入.)
有没有办法进行这种比较
我尝试使用(x < std::numeric_limits<double>::denorm_min())但是在使用SSE2数学时,这似乎显着减慢了循环.(我知道非正规可以减慢计算速度,但我没想到它们只是移动并比较慢.)
更新:
另一种方法是在比较之前使用volatile强制x进入内存,例如通过写入
} while (*((volatile double*)&x) <= 0.0);
Run Code Online (Sandbox Code Playgroud)
但是,根据应用程序和编译器应用的优化,此解决方案也会引入明显的开销.
更新: 任何容忍的问题是它是非常随意的,即它取决于具体的应用程序或上下文.我更愿意在没有过多精度的情况下进行比较,这样我就不必做任何额外的假设或在我的库函数的文档中引入一些任意的epsilons.
我正在使用一个双精度数组indata(在堆中,用malloc分配)和一个本地双精度调用sum.
我写了两个不同的函数来比较值indata,并获得不同的结果.最终我确定差异是由于一个函数在条件测试中使用表达式,而另一个函数在同一条件测试中使用局部变量.我希望这些是等价的.
我的功能A使用:
if (indata[i]+indata[j] > max) hi++;
Run Code Online (Sandbox Code Playgroud)
我的功能B使用:
sum = indata[i]+indata[j];
if (sum>max) hi++;
Run Code Online (Sandbox Code Playgroud)
经过相同的数据集之后max,我最终会得到不同的值,hi具体取决于我使用的功能.我认为功能B是正确的,功能A是误导性的.同样,当我尝试下面的代码片段时
sum = indata[i]+indata[j];
if ((indata[i]+indata[j]) != sum) etc.
Run Code Online (Sandbox Code Playgroud)
条件将评估为真.
虽然我理解浮点数不一定能提供精确的表示,但为什么在计算表达式vs存储在变量中时,精确表示会发生变化?建议的最佳做法是在条件之前始终评估这样的双重表达式吗?谢谢!
浮点表达式有时可以在处理硬件上收缩,例如使用融合乘法和加法作为单个硬件操作.
显然,使用这些不仅仅是一个实现细节,而是由编程语言规范控制.具体来说,C89标准不允许这样的收缩,而在C99中,只要定义了某些宏,就允许它们.请参阅此SO答案中的详细信息.
但是C++怎么样?浮点收缩是不允许的?某些标准允许吗?普遍允许吗?
我想得到一个确切/准确的答案,为什么下面的代码打印出不同的结果:
#include "stdio.h"
int main(void)
{
int a = 9;
int b = 10;
printf("%d\n", (double)a / (double)b == 0.9); /* prints 0 */
printf("%d\n", (double)9 / (double)10 == 0.9); /* prints 1 */
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我认为这可能是编译器依赖的,我的是gcc(GCC mingw Windows7)4.8.1和gcc(Debian 4.7.2-5)4.7.2.
非常感谢你!
UPDATE!
我使用和不使用-std = c99选项生成汇编代码,这应该有助于理解这里发生的事情.
没有-std = c99(这给出了结果0/1):
.file "a.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5 …Run Code Online (Sandbox Code Playgroud) 我最近分析了一个用VS2005编译的旧代码,因为"debug"(无优化)和"release"(/ O2/Oi/Ot选项)编译中的数字行为不同.(简化)代码如下所示:
void f(double x1, double y1, double x2, double y2)
{
double a1, a2, d;
a1 = atan2(y1,x1);
a2 = atan2(y2,x2);
d = a1 - a2;
if (d == 0.0) { // NOTE: I know that == on reals is "evil"!
printf("EQUAL!\n");
}
Run Code Online (Sandbox Code Playgroud)
f如果使用相同的值对(例如f(1,2,1,2))调用,则该函数应打印"EQUAL" ,但这并不总是发生在"release"中.实际上,编译器已经优化了代码,好像它是完全相同的d = a1-atan2(y2,x2)并且完全删除了对中间变量的赋值a2.此外,它利用了第二个atan2()结果已经在FPU堆栈上的事实,因此a1在FPU上重新加载并减去这些值.问题是FPU以扩展精度(80位)工作,而a1"仅"加倍(64位),因此将第一个atan2()结果保存在内存中实际上已经失去了精度.最终,d包含扩展精度和双精度之间的"转换错误".
我完全知道==应该避免使用float/double的身份(操作符).我的问题不是关于如何检查双打之间的接近程度.我的问题是如何考虑"契约"对局部变量的赋值.按照我的"天真"观点,赋值应强制编译器将值转换为变量类型表示的精度(在我的例子中为double).如果变量是"浮动"怎么办?如果他们是"int"(很奇怪,但合法)怎么办?
那么,简而言之,C标准对这些案例有何评价?