Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting and adjusting for negative zero

I have some code that has been port from Java to 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);

But in C++ r can be either +0.0 or -0.0, when r equals -0.0 it fails the check.

I've tried to adjust for negative zero in the code below but it never hits the DEBUG("Negative zero") line. But r2 does print out equal to +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);

Any suggestion?

TESTING CODE:

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);

TESTING RESULTS:

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

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) 

FLAGS:

CXXFLAGS += -g -Wall -fPIC

ANSWER:

I've used @amit's answer to do the following.

return (r>=(0.0-std::numeric_limits<double>::epsilon()));

Which seems to work.

like image 754
Justin Avatar asked Dec 07 '12 17:12

Justin


2 Answers

The OP mentions std::signbit in his question, but it should be in an answer to be found quickly, so here it is:

This works since C++11 to distinguish between -0.0 and +0.0:

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

std::signbit on cppreference

like image 70
Gabriel Devillers Avatar answered Oct 20 '22 13:10

Gabriel Devillers


On some older systems (i.e. pre-IEE754) you may find equality checks against 0 fail for negative-0:

if (a == 0.0) // when a==-0.0, fails

you can work around this by adding 0.0 to a value before the comparison:

if ((a+0.0) == 0.0) // when a == -0.0, succeeds

I would caution, however, that combinations of hardware/software that really require this are quite unusual. The last time I had to do it was on a Control Data mainframe. Even there, it only arose under somewhat unusual circumstances: the Fortran compiler allowed negative zeros to be generated, and knew to compensate for them in comparisons. The Pascal compiler generated code to turn negative zeros into normal zeros as part of a computation.

Therefore, if you wrote a routine in Fortran and called it from Pascal, you could run into this problem, and prevent it as above by adding 0.0 before doing a comparison.

I'm gonna put pretty good odds that your problem doesn't really stem from comparisons with negative zero though. All reasonably modern hardware of which I'm aware handles this entirely automatically, so software never has to consider it at all.

like image 40
Jerry Coffin Avatar answered Oct 20 '22 11:10

Jerry Coffin