检测和调整负零

新手上路,请多包涵

我有一些从 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);

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

我试图在下面的代码中调整负零,但它从未到达 DEBUG(“Negative zero”) 行。但是 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);

有什么建议吗?

测试代码:

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

测试结果:

 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

海合会:

 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)

旗帜:

 CXXFLAGS += -g -Wall -fPIC

回答:

我已经使用@amit 的答案来执行以下操作。

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

这似乎有效。

原文由 Justin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 755
2 个回答

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

在你的情况下:

 if (|r - 0.0| >= EPSILON)

其中 EPSILON 是您的容差因子,如果 r 不为 0.0,至少有 EPSILON 间隔,则结果为真。

原文由 amit 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

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

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

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


然而,我要提醒的是,真正需要这样做的硬件/软件组合是 非常 不寻常的。我最后一次必须这样做是在 Control Data 大型机上。即使在那里,它也只是在一些不寻常的情况下出现:Fortran 编译器允许生成负零,并且知道在比较中补偿它们。作为计算的一部分,Pascal 编译器生成了将负零转换为正常零的代码。

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

不过,我会给出很大的可能性,即您的问题并不是真正源于与负零的比较。我所知道的所有相当现代的硬件都会完全自动处理这个问题,所以软件根本不需要考虑它。

原文由 Jerry Coffin 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题