少しブール値と比較する


12

私がuint16_tでエンコードされたフラグのセットを持っているとしましょうflags。たとえば、AMAZING_FLAG = 0x02。今、私には機能があります。この関数は、フラグを変更するかどうかを確認する必要があります。変更する場合は、フラッシュに書き込む必要があるためです。そしてそれは高価です。したがって、flags & AMAZING_FLAGがに等しいかどうかを確認するチェックが必要ですdoSet。これが最初のアイデアです。

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

これは、直感的なifステートメントではありません。次のようなより良い方法があるはずだと思います。

if ((flags & AMAZING_FLAG) != doSet){

}

しかし、これは、実際に仕事をしないtrueに等しくなるように思えます0x01

それで、少しをブール値と比較するためのきちんとした方法はありますか?


2
ロジックが何である必要があるかは完全に明確ではありません。これ(flags & AMAZING_FLAG) && doSetですか?
カイルム

質問は明確ではありません。「フラグ」が0x01かどうかを確認します。よろしいですか?はいの場合、ビットごとの演算子「&」を使用できます。
Vikas Vijayan

doSetがtrueの場合にのみsetAmazingFlagを呼び出して読みやすくしたい場合は、関数名のチェックアウトを向上させます。そうでない場合は、名前が言うことを実行するかどうかにかかわらず、コードの読み取りが不正になる関数があります
AndersK

あなたがやろうとしているのがそれをもっと読みやすくすることだけなら、関数 `flagsNotEqual``を作るだけです。
Jeroen3

回答:


18

ゼロ以外の数値を1(true)に変換するには、古い手法があります。!(not)演算子を2回適用します。

if (!!(flags & AMAZING_FLAG) != doSet){

4
または(bool)(flags & AMAZING_FLAG) != doSet-私はもっと直接的だと思います。(これはMicrosoft氏に問題があることを示唆していますが)。また、((flags & AMAZING_FLAG) != 0)おそらくコンパイルされたものであり、完全に明示的です。
クリスホール

「また、((flags&AMAZING_FLAG)!= 0)は、おそらくコンパイル後のものであり、完全に明示的です」。でしょうか?doSetがtrueの場合はどうなりますか?
Cheiron

@ChrisHall(bool)2はCでは1になりますか、それとも2のままですか?
user253751

3
C11標準、6.3.1.2ブール型:「スカラー値が_Boolに変換されると、値が0と等しい場合、結果は0になります。それ以外の場合、結果は1になります。」そして6.5.4キャスト演算子:「括弧で囲まれた型名が式の前にあると、式の値が名前付き型に変換されます。」
クリスホール

@Cheiron、より明確に:((bool)(flags & AMAZING_FLAG) != doSet)と同じ効果が(((flags & AMAZING_FLAG) != 0) != doSet)あり、両方ともおそらくまったく同じものにコンパイルされます。提案されたもの(!!(flags & AMAZING_FLAG) != doSet)は同等であり、同じようにコンパイルされると思います。それはあなたがよりはっきりしていると思う人の好みの問題です:(bool)キャストは、キャストと_Boolへの変換がどのように機能するかを覚えておく必要があります。これ!!は他のいくつかの精神体操、またはあなたが「トリック」を知るために必要です。
クリスホール

2

ビットマスクをブール文に変換する必要があります。これは、Cでは値0またはと同等です1

  • (flags & AMAZING_FLAG) != 0。最も一般的な方法。

  • !!(flags & AMAZING_FLAG)。やや一般的で、使用も可能ですが、少し不可解です。

  • (bool)(flags & AMAZING_FLAG)。C99以降のモダンCウェイのみ。

上記のいずれかの方法を使用して、!=またはを使用してブール値と比較します==


1

論理的な観点からは、flags & AMAZING_FLAG他のすべてのフラグをマスクするビット操作のみです。結果は数値です。

ブール値を受け取るには、比較を使用します

(flags & AMAZING_FLAG) == AMAZING_FLAG

これで、この論理値をと比較できますdoSet

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

Cでは、数値からブール値への暗黙の変換規則のため、略語が存在する場合があります。だからあなたも書くことができます

if (!(flags & AMAZING_FLAG) == doSet)

もっと簡潔に書いてください。しかし、前のバージョンは読みやすさの点で優れています。


1

doSet値に基づいてマスクを作成できます。

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

これで、チェックは次のようになります。

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

一部のアーキテクチャで!!は、ブランチにコンパイルされる場合があり、これにより、2つのブランチがある場合があります。

  1. による正規化 !!(expr)
  2. と比較する doSet

私の提案の利点は、単一のブランチが保証されていることです。

注:左に30以上シフトして、未定義の動作を導入しないようにしてください(整数が32ビットであると想定)。これは、static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");


1
すべての方法のブール式は、結果としてブランチになる可能性があります。奇妙な手動最適化トリックを適用する前に、まず分解します。
ランディン

1
@Lundin、確かに、それが私が「いくつかのアーキテクチャについて」と書いた理由です...
Alex Lop。

1
これは主にコンパイラのオプティマイザの問題であり、ISA自体の問題ではありません。
ランディン

1
@Lundin同意しない。一部のアーキテクチャには条件付きビットセット/リセット用のISAがあり、その場合、ブラケットは必要ありませんが、他のアーキテクチャは必要ありません。godbolt.org/z/4FDzvw
Alex Lop。

注:あなたがこれを行う場合は、定義してくださいAMAZING_FLAGという点でAMAZING_FLAG_IDX(例えば#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))、あなたが1を更新することができることを、このような2つの場所で定義された同じデータを持っていないので(から、言う、)0x40x8他の(間)IDX2変更されません) 。
ShadowRanger

-1

このテストを実行するには複数の方法があります。

三項演算子は、コストのかかるジャンプを生成する可能性があります。

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))

ブール変換を使用することもできます。これは効率的な場合とそうでない場合があります。

if (!!(flags & AMAZING_FLAG) != doSet)

または同等の代替:

if (((flags & AMAZING_FLAG) != 0) != doSet)

乗算が安価な場合は、次のようにしてジャンプを回避できます。

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)

場合はflags、符号なしで、コンパイラは非常にスマートで、以下の部門では、単純なシフトにコンパイルされることがあります。

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)

アーキテクチャで2の補数演算を使用する場合、別の方法を次に示します。

if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))

代わりに、flagsビットフィールドを持つ構造体として定義して、はるかに単純で読みやすい構文を使用することもできます。

if (flags.amazing_flag != doSet)

悲しいかな、ビットフィールドの仕様ではビットレベルの実装を正確に制御できないため、このアプローチは通常は嫌われます。


コードの主要な目的は可読性であるべきですが、ほとんどの例ではそうではありません。実際にサンプルをコンパイラーにロードすると、さらに大きな問題が発生します。最初の2つのサンプル実装が最高のパフォーマンスを発揮します。最小のジャンプとロード(ARM 8.2、-O)(これらは同等です)。他のすべての実装はパフォーマンスが低下します。一般に、コンパイラーが何が起こっているのか理解しにくくなり、パフォーマンスが低下するため、手の込んだこと(マイクロ最適化)を行わないようにする必要があります。したがって、-1。
Cheiron
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.