2

可能重复:
C 编译器错误(浮点运算)?

我有两个双打,我可以保证完全等于 150 位小数 - 即。以下代码:

printf("***current line time is %5.150lf\n", current_line->time);
printf("***time for comparison is %5.150lf\n", (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS));

...返回:

***current line time is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
***time for comparison is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

FIVE_MINUTES_IN_DAYS是 #defined,并且current_line->timelast_stage_four_print_time都是双精度数。

我的问题是我的调试代码的下一行:

printf("if condition is %d\n", (current_line->time >= (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS)));

返回以下内容:

if condition is 0

谁能告诉我这里发生了什么?我知道非小数/不精确的性质,floatsdoubles它们根本不会出现任何错误(原始数字都已使用sscanf或#defined 读取,并且都指定到小数点后 10 位)。

编辑:我的错误是假设printf-ing 双打在内存中准确地表示了它们,这是错误的,因为正在计算一个值。声明(last_stage_four_print_time + FIVE_MINUTES_IN_DAYS)threshold_time并使用它来解决问题。我将确保使用 epsilon 进行比较 - 我知道这是要走的路,我只是对为什么我(错误地)认为看起来相同的这些值显然不相等感到困惑。

4

6 回答 6

4

浮点数肯定不准确到 150 位有效数字,所以我不确定从“视觉”比较(如果有的话)中可以得出什么结论。

另一方面,这些值显然不是完全相同的(它们怎么可能是相同的,因为其中一个是通过加法当场计算的?)。所以不清楚为什么你看到的行为是意外的。

永远不要像那样比较浮点数,只需对差异与 epsilon 进行标准比较。

于 2012-04-04T06:58:01.610 回答
4

阅读浮点表示(特别是http://en.wikipedia.org/wiki/IEEE_754-2008)。尝试将包含双精度的字节的实际内容打印为十六进制,它们不会逐位匹配。浮点数的正确比较在 Knuth(半数字算法)中。很简单(用 int 代替 bool,用 double 代替 float,用 1 代替 true):

bool almostEqual(float x, float y, float epsilon)
{
    if (x == 0.0 && y == 0.0) {
        return true;
    }

    if (fabs(x) > fabs(y)) {
        return fabs((x - y) / x) < epsilon;
    } else {
        return fabs((x - y) / y) < epsilon;
    }
}
于 2012-04-04T07:00:54.323 回答
3

您应该始终使用 EPSILON 值来比较浮点数和双精度数以检查是否相等。即使看起来相同,内部表示也不能保证匹配,因为这些类型的数字以二进制表示。

你可以做类似的事情

#define EPSILON 0.00001
...
if (fabs(a - b) <= EPSILON) return 1; // they are equal
return 0;
于 2012-04-04T06:56:28.063 回答
2

耶稣关于如何解决这个问题是正确的。

至于为什么......在一种情况下您读取一个常量值,在另一种情况下您执行加法运算。即使打印输出完全相同,二进制表示也可能略有不同。

尝试检查支持两个双精度的内存,看看是否有任何位不同(会有差异)。

对于综合治疗,我建议

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

于 2012-04-04T06:59:23.557 回答
1

一般来说,您不应该使用 == 来比较浮点数或双精度数。相反,您应该检查差异是否小于某个小的数字。

double second_number = last_stage_four_print_time + FIVE_MINUTES_IN_DAYS;
if (fabs(current_line->time - second_number) < 0.001 || current_line->time > second_number){
  // your comparison code
}
于 2012-04-04T06:59:26.373 回答
1

首先,双打只有 15-16 位小数(52 位 matissa 的 log_2)。

其次,如果要比较,请使用已经提到的 epsilon。

第三,为了调试,打印十六进制值。

于 2012-04-04T07:00:43.537 回答