精确为零的float / double相等

时间:2012-03-03 00:57:51

标签: c++ floating-point

我有一个使用floatsdoubles执行某些计算的算法。

示例:

double a;
double b;
double c;
...
double result = c / (b - a);
if ((result > 0) && (result < small_number))
{
    // result is relevant...
} else {
    // result not required...
}

现在,我担心(b - a)可能为零。如果它接近于零而不是零,则无关紧要,因为result将超出范围有用,并且我已经检测到(当(b - a)接近零时,result将接近+/- inf,这不在0 - small_number范围内......)

但如果(b - a)的结果恰好为零,我预计由于除以零会发生某些平台依赖。我可以将if语句更改为:

if ((!((b-a) == 0.0)) && ((result = c/(b-a)) > 0) && (result < small_number)) {

但我不知道(b-a) == 0.0是否始终会检测到零的相等性。我已经看到浮点数中存在多个精确零点的表示形式?如何在不进行ε检查的情况下测试它们,我不需要(在我的算法中会忽略一个小的epsilon)?

什么是独立的平台检查方式?

修改

不确定人们是否足够清楚。基本上我想知道如何找到如下表达式:

double result = numerator / denominator;

将导致浮点异常,cpu异常,来自操作系统的信号或其他内容....没有实际执行操作并查看它是否会“抛出”...因为检测到“抛出”这种性质似乎是复杂的和平台特定的。

( (denominator==0.0) || (denominator==-0.0) ) ? "Will 'throw'" : "Won't 'throw'";是否足够?

8 个答案:

答案 0 :(得分:9)

这取决于ba获取其值的方式。零具有浮点格式的精确表示,但更大的问题是几乎 - 但不是非常零的值。检查始终是安全的:

if (abs(b-a) > 0.00000001  && ...

0.00000001是值得的值。

答案 1 :(得分:4)

以下是您的操作方法:检查

,而不是检查(result < small_number)
(abs(c) < abs(b - a) * small_number)

然后你所有的麻烦都消失了!如果通过此测试,c/(b-a)的计算将永远不会溢出。

答案 2 :(得分:2)

我猜你可以使用fpclassify(-0.0) == FP_ZERO。但这只有在你想检查某人是否确实将某种零值放入float-type变量时才有用。正如许多人已经说过的,如果你想检查计算结果,由于表示的性质,你可能会得到非常接近于零的值。

答案 3 :(得分:0)

更新(2016-01-04)

我收到了一些关于这个答案的downvotes,我想知道我是否应该删除它。似乎共识(https://meta.stackexchange.com/questions/146403/should-i-delete-my-answers)只是在极端情况下才能删除答案。

所以,我的回答是错误的。但我想我会把它放弃,因为它提供了一种有趣的“思考开箱即用”的思想实验。

===============

宾果,

你说你想知道b-a == 0。

另一种看待这种情况的方法是确定a == b。如果a等于b,那么b-a将等于0.

我发现了另一个有趣的想法:

http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm

基本上,你采用你拥有的浮点变量并告诉编译器将它们(一点一滴地)重新解释为有符号整数,如下所示:

if (*(int*)&b == *(int*)&a)

然后你要比较整数,而不是浮点数。也许这会有所帮助?也许不吧。祝你好运!

答案 4 :(得分:0)

我认为(b-a)==0c/(b-a)由于(b-a)为零而失败的情况下将完全正确。浮动数学是棘手的,但在我看来质疑这是夸大其词。另外,我相信(b-a)==0将等同于b!=a

区分正面和负面0也不是必需的。参见例如这里Does float have a negative zero? (-0f)

答案 5 :(得分:0)

对于epsilon,有一个标准模板定义std :: numeric_limits :: epsilon()。我想检查差异大于std :: numeric_limits :: epsilon()应该足够安全以防止被零除。我猜这里没有平台依赖。

答案 6 :(得分:0)

简而言之,如果我们知道它代表格式,我们就可以知道浮动数字是零。

在实践中,我们将x与较小的数字进行比较。如果x小于此数字,我们认为x与ZERO功能相同(但大部分时间我们的小数仍然大于零)。这种方法非常简单,高效且可以跨平台。

实际上floatdouble以特殊格式呈现,广泛使用的是当前硬件IEEE 754,将数字划分为符号,指数和尾数(有效数字)位。

因此,如果我们想要检查浮点数是否为 ZERO ,我们可以检查指数和尾数是否为零,请参阅here

  

在IEEE 754二进制浮点数中,表示零值   有偏差的指数和有效数都为零。负零   将符号位设置为1。

float为例,我们可以编写一个简单的代码来提取指数和尾数位,然后检查它。

#include <stdio.h>   

typedef union {
          float f;
          struct {
              unsigned int mantissa : 23;
              unsigned int exponent : 8;
              unsigned int sign :     1;
          } parts;
} float_cast;


int isZero(float num) {

    int flag = 0;
    float_cast data;
    data.f = num;

    // Check both exponent and mantissa parts
    if(data.parts.exponent == 0u && data.parts.mantissa == 0u) {
       flag = 1;
    } else {
       flag = 0;
   }

   return(flag);
}


int main() {

   float num1 = 0.f, num2 = -0.f, num3 = 1.2f;

   printf("\n is zero of %f -> %d", num1, isZero(num1));
   printf("\n is zero of %f -> %d", num2, isZero(num2));
   printf("\n is zero of %f -> %d", num3, isZero(num3));

   return(0);
}

测试结果:

  

#是0.000000的零 - &gt; 1
   #是-0.000000的零 - &gt; 1
   #是1.200000的零 - &gt; 0

更多示例:

让我们检查float何时成为真正的ZERO代码。

void test() {
     int i =0;
     float e = 1.f, small = 1.f;
     for(i = 0; i < 40; i++) {
         e *= 10.f;
         small = 1.f/e;
         printf("\nis %e zero? : %d", small, isZero(small));
     }
     return;
}


is 1.0000e-01 zero? : NO
is 1.0000e-02 zero? : NO
is 1.0000e-03 zero? : NO
is 1.0000e-04 zero? : NO
is 1.0000e-05 zero? : NO
is 1.0000e-06 zero? : NO
is 1.0000e-07 zero? : NO
is 1.0000e-08 zero? : NO
is 1.0000e-09 zero? : NO
is 1.0000e-10 zero? : NO
is 1.0000e-11 zero? : NO
is 1.0000e-12 zero? : NO
is 1.0000e-13 zero? : NO
is 1.0000e-14 zero? : NO
is 1.0000e-15 zero? : NO
is 1.0000e-16 zero? : NO
is 1.0000e-17 zero? : NO
is 1.0000e-18 zero? : NO
is 1.0000e-19 zero? : NO
is 1.0000e-20 zero? : NO
is 1.0000e-21 zero? : NO
is 1.0000e-22 zero? : NO
is 1.0000e-23 zero? : NO
is 1.0000e-24 zero? : NO
is 1.0000e-25 zero? : NO
is 1.0000e-26 zero? : NO
is 1.0000e-27 zero? : NO
is 1.0000e-28 zero? : NO
is 1.0000e-29 zero? : NO
is 1.0000e-30 zero? : NO
is 1.0000e-31 zero? : NO
is 1.0000e-32 zero? : NO
is 1.0000e-33 zero? : NO
is 1.0000e-34 zero? : NO
is 1.0000e-35 zero? : NO
is 1.0000e-36 zero? : NO
is 1.0000e-37 zero? : NO
is 1.0000e-38 zero? : NO
is 0.0000e+00 zero? : YES   <-- 1e-39
is 0.0000e+00 zero? : YES   <-- 1e-40

答案 7 :(得分:-1)

你可以尝试

if ((b-a)!=(a-b) && ((result = c/(b-a)) > 0) && (result < small_number))) {
...