2017-02-06 19 views
0

浮動小数点値のXORスワッピングアルゴリズムをC++で使用することはできますか?同じデータ型浮動小数点値のC++ XORスワップ

を持つ個別の変数の値を交換する

のXORビット演算が、私は少し混乱しています:

しかしウィキペディアは言います。このコードで:

void xorSwap (int* x, int* y) { 
    *x ^= *y; 
    *y ^= *x; 
    *x ^= *y; 
} 

int main() { 

    float a = 255.33333f; 
    float b = 0.123023f; 
    xorSwap(reinterpret_cast<int*>(&a), reinterpret_cast<int*>(&b)); 
    std::cout << a << ", " << b << "\n"; 

    return 0; 
} 

(少なくともgccの下では)動作するようですが、必要ならばそのような練習が許されていますか?

+1

あなたの浮動小数点変数のデータはちょうど上の他のデータと値のように、単に0と1として格納されていることを忘れないでくださいバイナリコンピュータ。ビットの操作は、そのビットが何を表しているかにかかわらず、まったく同じです。 –

+0

何が問題なのですか? @Someprogrammerdudeはこう言っています:D – ForceBru

+3

'xor'トリックを使わないでください。Wikipediaからのあなたの引用は可能です。あなたが余分な整数(そして現代の組み込みシステムでさえメモリのbucketloadsを持っている)を手に入れることができないほどのメモリがない限り、それは役に立たず、おそらく単純な一時変数の交換よりも遅いかもしれません。それはまだ私たちの古き民の時代に降格されなければなりません。彼らは1K ZX80にコードを詰め込むことを心から覚えています:-) – paxdiablo

答えて

6

技術的には、ですが、IInspectableによって明確にコメントされているため、未定義の動作が発生します。
とにかく、代わりにstd::swapを使用することをお勧めします。これは、特定のデータ型に特化したテンプレートであり、うまく機能するように設計されています。

+2

§3.10p 10 [basic.lval]:* "プログラムが以下のタイプのglvalue以外の値でオブジェクトの格納された値にアクセスしようとすると、その動作は定義されません:[*]" Soはい、可能、もっともらしく、きれいで清潔です。そして間違っている。これは未定義です。 – IInspectable

+0

申し訳ありませんが、未定義の動作は発生しません。それは簡単にはできません、アルゴリズムは、スワップされた情報のバイナリ解釈とは独立しています。バイトパターンは、割り当ての場合と同じようにスワップされます。私たちはUBの問題で遠すぎると思います、私は恐れます。それは、浮動小数点のビットがビットとして送信され、二重としてではないので、comms行を通してビットパターンを送信することは未定義の振る舞いをするということです。 –

1

intfloatと同じサイズであれば、実際の合理的なアーキテクチャであれば実際に動作します。浮動小数点数のメモリはビットの集合です。これらのビットを解釈し、xor演算を使用してそれらを完全に入れ替えます。これらのビットを適切なfloatとして再び使用できます。参照する見積もりは、交換する2つの値が同じタイプである必要があり、どちらもintです。

しかし、アーキテクチャによっては、さまざまな種類のレジスタ間での移動、または明示的にレジスタをメモリにフラッシュする可能性があります。最近、正真正銘の最適化コンパイラを使って、ほとんどすべての正常なアーキテクチャーで見られるように、std::swapや一時変数を持つ式を使用した明示的なスワップが実際に高速です。

I.e.次のように記述する必要があります好ましく

float a = 255.33333f; 
float b = 0.123023f; 
float tmp = a; 
a = b; 
b = tmp; 

かを:あなたのアーキテクチャの標準ライブラリの作者が実際に有益であることがスワッピングXORを決定した場合

float a = 255.33333f; 
float b = 0.123023f; 
std::swap(a,b); 

、あなたは最後の形式は、それを使用することを願っていなければなりません。 xorのやりとりは、不必要に不可解な実装に意図を隠すという点で典型的な悪いイディオムです。悪質なオプティマイザを使用して真剣に登録が不足しているケースでは効率的でした。

+0

xor-trickは、intとfloatのサイズが同じでintがより厳密な配置要件を持つ場合にも失敗します。私はそれがありそうもないと認めます。 –

+0

@ HansOlsson:逆に、専用のFPUサーキットリグでは、浮動小数点型と整数型の間で異なる整列要件が存在する可能性が高くなります。 FPUの計算は多くの場合、SIMDユニットで実行されるため、通常は汎用CPUレジスタとは異なるアラインメントが必要です。 – IInspectable

+0

@IInspectable特殊なSIMDユニットは浮動小数点数の方が厳密な整列が可能ですが、intの整列要件が厳しい場合にのみxor-trickの問題が発生します。 –

1

あなたのコードは、未定義の動作を呼び出します。 CまたはC++ではfloat*int*にキャストしてそのまま使用することはできません。 reinterpret_castは、互換性のあるレイアウトを持つ無関係の構造体間の変換、または型付きポインタとvoid*の間の一時的な変換に使用する必要があります。

ああ、この特定のケースでは、UBは単なる学問的な問題ではありません。コンパイラは、xorSwap()floatには触れず、言語のエイリアス規則で許容される最適化を実行し、スワップされた値の代わりにabの元の値を出力することがあります。そして、それはintfloatが異なるサイズやアラインメントのアーキテクチャになっていません。

フロートからunsigned char配列にmemcpy()を、ループでXORを実行してからmemcpy()に戻す必要があります。もちろん、通常のスワップよりも動作を遅くします。もちろん、xorベースのスワッピングは、通常のスワッピングよりも遅いです。

+0

"型付きポインタとvoid *"の間で変換するには、 'reinterpret_cast'を使用する必要があります。そのために 'static_cast'を使用しませんか? – user2079303

+0

@ user2079303この2つは同等の機能を持っていますが、私は状況に適したレベルまで恐ろしく見えるので、 'reinterpret_cast'を好む傾向があります。もちろん、最初はC++で 'void *'を使用するのはまれですので、質問はしばしば出てくることはありません。 – Sneftel

1

a)コンパイラが許可する場合に可能です。

b)の標準は、gccの上で何をしたい正確に述べるよりも実際に少ない効率的な行動(すなわち、未定義の動作)

C)を定義しないため、操作:

を与えられた:

void xorSwap (unsigned int* x, unsigned int* y) { 
    *x ^= *y; 
    *y ^= *x; 
    *x ^= *y; 
} 

void swapit3(float& a, float&b) 
{ 
    xorSwap(reinterpret_cast<unsigned int*>(&a), reinterpret_cast<unsigned int*>(&b)); 
} 
この中

結果:この一方

swapit3(float&, float&):       # @swapit3(float&, float&) 
     mov  eax, dword ptr [rdi] 
     xor  eax, dword ptr [rsi] 
     mov  dword ptr [rdi], eax 
     xor  eax, dword ptr [rsi] 
     mov  dword ptr [rsi], eax 
     xor  dword ptr [rdi], eax 
     ret 

:0この中の

void swapit2(float& a, float&b) 
{ 
    std::swap(a,b); 
} 

結果:

swapit2(float&, float&):       # @swapit2(float&, float&) 
     mov  eax, dword ptr [rdi] 
     mov  ecx, dword ptr [rsi] 
     mov  dword ptr [rdi], ecx 
     mov  dword ptr [rsi], eax 
     ret 

リンク:https://godbolt.org/g/K4cazx

関連する問題