没有epsilon可以将浮点数与0.0进行比较吗?

时间:2016-08-23 18:38:19

标签: c++ floating-point equality

我知道,要比较两个浮点值,需要使用一些epsilon精度,因为它们并不精确。但是,我想知道是否存在边缘情况,我不需要那个epsilon。

特别是,我想知道做这样的事情是否总是安全的:

double foo(double x){
    if (x < 0.0) return 0.0;
    else return somethingelse(x); // somethingelse(x) != 0.0
}

int main(){
   int x = -3.0;
   if (foo(x) == 0.0) { 
     std::cout << "^- is this comparison ok?" << std::endl; 
   }
}

我知道有更好的方法来编写foo(例如,另外返回一个标志),但我想知道一般情况下是否可以将0.0分配给浮点变量并稍后进行比较它到0.0

或者更一般的是,以下比较是否始终为真?

double x = 3.3;
double y = 3.3;
if (x == y) { std::cout << "is an epsilon required here?" << std::endl; }

当我尝试它时,它似乎有效,但可能不应该依赖它。

5 个答案:

答案 0 :(得分:5)

是的,如果您返回0.0,则可以将其与0.0进行比较; 0可以完全表示为浮点值。如果你返回3.3,你必须要小心,因为3.3不能完全表示,所以从double到float的转换会产生不同的值。

答案 1 :(得分:3)

是的,在这个例子中,检查== 0.0是完全正常的。这不是因为0.0在任何方面都很特殊,而是因为您只指定一个值并在之后进行比较。您也可以将其设置为3.3并比较== 3.3,这也可以。您正在存储位模式,并比较完全相同的位模式,只要这些值不会被提升为另一种类型进行比较。

但是,在数学上等于零的计算结果并不总是等于0.0

此Q / A已演变为包括程序的不同部分由不同编译器编译的情况。问题没有提到这一点,我的答案仅适用于所有相关部分使用相同的编译器。

C ++ 11标准,
§5.10平等运营商

  

6如果两个操作数都是算术或枚举类型,则通常   对两个操作数执行算术转换;每一个   如果指定的关系为真,则运算符应为真   如果是假的,则为false。

这种关系没有进一步定义,所以我们必须使用“平等”的共同含义。

§2.13.4浮动文字

  

1 [...]如果缩放值在可表示值的范围内   对于它的类型,结果是可表示的缩放值,否则   最接近缩放值的较大或较小的可表示值,   以实现定义的方式选择。 [...]

当转换文字时,编译器必须在两个值之间进行选择,此时值不可表示。如果为同一个文字选择相同的值,则可以安全地比较3.3等值,因为==表示“相等”。

答案 2 :(得分:2)

更正: 0因为浮点值不是唯一的,但是IEEE 754将比较0.0==-0.0定义为真(对于那个问题,任何零)。

因此,使用0.0这适用于 - 对于其他所有数字,它不起作用。一个编译单元(例如库)和另一个(例如您的应用程序)中的文字3.3可能不同。该标准只要求编译器使用它在运行时使用的相同舍入 - 但不同的编译器/编译器设置可能使用不同的舍入。

它会在大部分时间(0)工作,但是练习非常糟糕。

只要您使用具有相同设置的相同编译器(例如一个编译单元),它就可以工作,因为文字0.00.0f每次都会转换为相同的位模式。零的表示虽然不是唯一的。因此,如果在库中声明foo并且在某个应用程序中对它进行调用,则相同的函数可能会失败。

你可以使用std::fpclassify来检查这种情况,以检查返回的值是否代表零。对于每个有限(非零)值,您将不得不使用epsilon比较,除非您保持在一个编译单元内并且不对值执行任何操作。

答案 3 :(得分:1)

正如在两种情况下所写的那样,您在同一个文件中使用相同的常量。编译器使用的字符串到浮点转换应该返回相同的位模式,因此它们不仅应该等于零件的加号或减号情况,而是一点一点地相等。

你是否有一个使用操作系统C库生成位模式的常量然后有一个字符串到f或者如果二进制文件被传输到另一台计算机而不是编译的那个计算机可能使用不同的C库的东西。你可能有问题。

当然,如果你为其中一个术语计算3.3,运行时间,并且再次使用另外3.3个计算的编译时间,你可以并且将在相等的比较中得到失败。有些常数显然比其他常量更有效。

当然,编写3.3比较是死代码,如果启用了优化,编译器就会删除它。

你没有指定浮点格式,也没有为你感兴趣的格式指定标准。有些格式有+/-零问题,有些不是例如。

答案 4 :(得分:0)

一个常见的误解是浮点值“不精确”。实际上,它们中的每一个都是完全精确的(除了-0.0或Inf可能是某些特殊情况),并且等于 s ·2 e –(p – 1)< / i> ,其中 s e p 分别是有效数,指数和精度,它们每个都是整数。例如。以IEEE 754-2008 binary32格式(aka float32) p = 24和1表示为‭0x‭800000‬‬·2 0 – 23 。当您处理浮点值时,有两点确实不正确:

  1. 使用FP一个实数表示。显然,并不是所有的实数都可以使用给定的FP格式表示,因此必须以某种方式将它们四舍五入。有几种舍入模式,但是最常用的是“舍入到最接近,并保持到偶数”。如果始终使用相同的舍入模式(几乎可以肯定),则始终使用相同的FP 1表示相同的实际值。因此,您可以确定,如果两个实数值相等,则它们的FP对应物也完全相等(但显然不是相反的)。
  2. 具有FP编号的操作(大部分)不准确。因此,如果您在计算机中实现了一些实值函数φξ)作为FP参数 f x ),并且想要将其结果与某个“真”值 y 进行比较,则需要使用一些ε进行比较,因为这非常困难(有时甚至是不可能的)将功能精确地赋予 y 的白色。并且ε的值在很大程度上取决于所涉及的FP操作的性质,因此在每种特定情况下,最优值可能都不同。

有关更多详细信息,请参阅D. Goldberg。每个计算机科学家应该了解的浮点运算法则和J.-M.穆勒等。浮点算法手册。您可以在Internet上找到这两种文本。