Detecting and adjusting for negative zero

我只是一个虾纸丫 提交于 2019-12-01 03:48:31

问题


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.


回答1:


Well, a generic suggestion when using doubles is remembering they are not exact. Thus, if equality is important - using some tolerance factor is usually advised.

In your case:

if (|r - 0.0| >= EPSILON)

where EPSILON is your tolerance factor, will yield true if r is not 0.0, with at least EPSILON interval.




回答2:


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.




回答3:


Presumably you meant something like if (r2==-0.0). Nonetheless, both negative 0 and positive 0 will compare equal. For all intents and purposes there's no difference between the two. You probably don't need to have a special case for negative 0. Your comparison r >= 0 should be true for either negative or positive 0.




回答4:


Consider:

#include <stdio.h>
#include <iostream>
using namespace std;

int main()
{
    double const x = -2.78269e-07;

    printf( "printf: x=%f\n", x );
    cout << "cout: x=" << x << endl;
}

With result (using Visual C++ 11.0):

[D:\dev\test]
> cl foo.cpp
foo.cpp

[D:\dev\test]
> foo
printf: x=-0.000000
cout: x=-2.78269e-007

[D:\dev\test]
> _

This seems awfully similar to the mysterious result in the question.

It's my considered opinion that it quacks like the question's result, looks like the question's result and waddles like the question's result.

So, I believe that the not shown computation code produced the value 2.78269e-007.


So, in conclusion, it was apparently only the described behavior, about “negative zero” comparing unequal to zero, that was non-standard. In reality there is apparently no negative zero, just a very small negative value. Which with the given output format is presented as all zero digits with a minus sign in front.




回答5:


It's just a very small negative number that the console couldn't print it for you
You can try checking for it depending on your precision so you can replace it with a pure zero.

std::cout << std::setprecision(7) << (abs(value) < 0.0000005f ? 0 : value);

Notice how I added 7 places to the floating precision as I specified with std::setprecision().

It depends on how accurately you want to print your floats/double.



来源:https://stackoverflow.com/questions/13767744/detecting-and-adjusting-for-negative-zero

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!