9

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

ただし、C ++では、またはのrいずれ+0.0-0.0になります。r等しい-0.0場合、チェックに失敗します。

以下のコードで負のゼロを調整しようとしましたが、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

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) 

フラグ:

CXXFLAGS += -g -Wall -fPIC

答え:

私は@amitの答えを使って次のことをしました。

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

これはうまくいくようです。

4

6 に答える 6

10

まあ、sを使用するときの一般的な提案doubleは、それらが正確ではないことを覚えていることです。したがって、平等が重要な場合は、通常、許容係数を使用することをお勧めします。

あなたの場合:

if (|r - 0.0| >= EPSILON)

ここで、EPSILONは許容係数であり、がr0.0でない場合、少なくともEPSILON間隔を置いてtrueを返します。

于 2012-12-07T17:12:09.200 に答える
10

一部の古いシステム(つまり、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

ただし、これを実際に必要とするハードウェア/ソフトウェアの組み合わせは非常に珍しいことに注意してください。私が最後にそれをしなければならなかったのは、コントロールデータのメインフレームでした。そこでも、それはやや珍しい状況でのみ発生しました。Fortranコンパイラーは負のゼロの生成を許可し、比較でそれらを補正することを知っていました。Pascalコンパイラは、計算の一部として負のゼロを通常のゼロに変換するコードを生成しました。

したがって、Fortranでルーチンを作成し、Pascalから呼び出した場合、この問題が発生する可能性があり、比較を行う前に0.0を追加することで上記のように防ぐことができます。

ただし、問題が実際には負のゼロとの比較に起因するものではないというかなりの確率を示します。私が知っているすべての合理的に最新のハードウェアはこれを完全に自動的に処理するので、ソフトウェアはそれをまったく考慮する必要はありません。

于 2012-12-07T17:50:54.497 に答える
5

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

cppreferenceのstd::signbit

于 2020-08-17T13:40:18.560 に答える
3

おそらくあなたはのようなものを意味しましたif (r2==-0.0)。それでも、負の0と正の0はどちらも等しく比較されます。すべての意図と目的のために、2つの間に違いはありません。おそらく、負の0に対して特別なケースを用意する必要はありません。負または正の0については、比較が真であるr >= 0 必要があります。

于 2012-12-07T17:13:50.550 に答える
1

検討:

#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;
}

結果付き(Visual C ++ 11.0を使用):

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

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

[D:\ dev \ test]
> _

これは、質問の不思議な結果と非常に似ているようです。

それは質問の結果のようにいじくり回し、質問の結果のように見え、質問の結果のようにぐらつくというのが私の考えです。

したがって、表示されていない計算コードが値を生成したと思います2.78269e-007


したがって、結論として、それは明らかに、ゼロと等しくないことを比較する「負のゼロ」についての記述された振る舞いだけであり、それは非標準でした。実際には、明らかに負のゼロはなく、非常に小さな負の値だけです。指定された出力形式では、前にマイナス記号が付いたすべてゼロの数字として表示されます。

于 2012-12-07T17:56:13.243 に答える
0

コンソールが出力できなかったのは非常に小さな負の数です。精度に応じてチェックしてみて、純粋なゼロ
に置き換えることができます。

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

で指定したように、浮動小数点精度に7桁を追加したことに注目std::setprecision()してください。

フロート/ダブルをどれだけ正確に印刷するかによって異なります。

于 2018-07-19T09:10:03.323 に答える