检测并调整负零点

Jus*_*tin 5 c++ algorithm negative-number

我有一些从Java到C++的代码

// since this point is a vector from (0,0,0), we can just take the
// dot product and compare
double r = point.dot(normal);
return (r>=0.0);
Run Code Online (Sandbox Code Playgroud)

但是在C++ r中可以是+0.0或者-0.0,当r等于-0.0它时,检查失败了.

我试图在下面的代码中调整负零,但它从未达到DEBUG("负零")线.但r2打印出来的等于+0.0.

// since this point is a vector from (0,0,0), we can just take the
// dot product and compare
double r = point.dot(normal);
if (std::signbit(r)){
    double r2 = r*-1;
    DEBUG("r=%f r=%f", r,r2);
    if (r2==0.0) {
        DEBUG("Negative zero");
        r = 0.0; //Handle negative zero
    }
}
return (r>=0.0);
Run Code Online (Sandbox Code Playgroud)

有什么建议吗?

测试代码:

DEBUG("point=%s", point.toString().c_str());
DEBUG("normal=%s", normal->toString().c_str());
double r = point.dot(normal);
DEBUG("r=%f", r);
bool b = (r>=0.0);
DEBUG("b=%u", b);
Run Code Online (Sandbox Code Playgroud)

测试结果:

DEBUG - point=Vector3D[ x=1,y=0,z=0 ]
DEBUG - normal=Vector3D[ x=0,y=-0.0348995,z=0.0348782 ]
DEBUG - r=0.000000
DEBUG - b=1
DEBUG - point=Vector3D[ x=1,y=0,z=0 ]
DEBUG - normal=Vector3D[ x=-2.78269e-07,y=0.0174577,z=-0.0174391 ]
DEBUG - r=-0.000000
DEBUG - b=0
Run Code Online (Sandbox Code Playgroud)

GCC:

Target: x86_64-linux-gnu
--enable-languages=c,c++,fortran,objc,obj-c++ 
--prefix=/usr 
--program-suffix=-4.6 
--enable-shared 
--enable-linker-build-id 
--with-system-zlib 
--libexecdir=/usr/lib 
--without-included-gettext 
--enable-threads=posix 
--with-gxx-include-dir=/usr/include/c++/4.6 
--libdir=/usr/lib 
--enable-nls 
--with-sysroot=/ 
--enable-clocale=gnu 
--enable-libstdcxx-debug 
--enable-libstdcxx-time=yes 
--enable-gnu-unique-object 
--enable-plugin 
--enable-objc-gc 
--disable-werror 
--with-arch-32=i686 
--with-tune=generic 
--enable-checking=release 
--build=x86_64-linux-gnu 
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 
Run Code Online (Sandbox Code Playgroud)

标志:

CXXFLAGS += -g -Wall -fPIC
Run Code Online (Sandbox Code Playgroud)

回答:

我用@ amit的答案做了以下事情.

return (r>=(0.0-std::numeric_limits<double>::epsilon()));
Run Code Online (Sandbox Code Playgroud)

这似乎有效.

ami*_*mit 10

好吧,使用doubles 时的一般建议是记住它们并不精确.因此,如果平等很重要 - 通常建议使用一些容差因子.

在你的情况下:

if (|r - 0.0| >= EPSILON)
Run Code Online (Sandbox Code Playgroud)

EPSILON你的公差因子在哪里,如果r不是0.0,将产生真,至少EPSILON间隔.


Jer*_*fin 7

在一些较旧的系统(即IEE754之前的版本)上,您可能会发现针对0的等式检查失败为负0:

if (a == 0.0) // when a==-0.0, fails
Run Code Online (Sandbox Code Playgroud)

您可以通过在比较之前将值添加0.0来解决此问题:

if ((a+0.0) == 0.0) // when a == -0.0, succeeds
Run Code Online (Sandbox Code Playgroud)

但是,我要提醒一下,确实需要这种硬件/软件的组合非常不寻常.我最后一次不得不这样做是在Control Data大型机上.即使在那里,它只在某些不寻常的情况下出现:Fortran编译器允许生成负零,并且知道在比较中补偿它们.Pascal编译器生成的代码将负零变为普通零作为计算的一部分.

因此,如果您在Fortran中编写了一个例程并从Pascal调用它,则可能会遇到此问题,并在进行比较之前通过添加0.0来阻止它.

我会提出相当不错的几点,你的问题并不是真正源于与负零的比较.我所知道的所有合理的现代硬件都是完全自动处理的,因此软件根本不需要考虑它.


Gab*_*ers 7

OP提到std::signbit在他的问题中提到,但应该在快速找到的答案中,所以这里是:

从 C++11 开始,这可以区分-0.0和+0.0:

#include <cmath>
std::signbit(x) // true iif sign bit of x is set i.e. x is negative
Run Code Online (Sandbox Code Playgroud)

cppreference 上的 std::signbit