7

次の if ステートメントを削除して、値が 0 未満かどうかを確認する方法はありますか?

int a = 100;
int b = 200;
int c = a - b;

if (c < 0)
{
    c += 3600;
}

の値はc0 から 3600 の間でなければなりません。 と は両方ともa符号b付きです。の値aも 0 から 3600 の間になければなりません (はい、これは 0.1 度単位のカウント値です)。値は割り込みによって 3600 にリセットされますが、その割り込みが遅すぎるとアンダーフローしますが、これは問題ではありませんが、ソフトウェアはそれを処理できるはずです。それは何をしますか。

このif (c < 0)チェックは、位置を計算しているかなりの場所で行います。(新しい位置の計算など)

私たちのコンパイラ(C89)が被除数の符号を使用している除数の符号を使用するために、Pythonのモジュロ演算子に慣れていました。

この計算を別の方法で行う方法はありますか?結果の例:

 a  -  b  = c
100 - 200 = 3500  
200 - 100 = 100
4

5 に答える 5

11

良い質問!これはどう?

c += 3600 * (c < 0);

これは、分岐予測スロットを保持する 1 つの方法です。

于 2013-07-19T15:37:11.047 に答える
7

これはどうですか(32ビットintを想定):

c += 3600 & (c >> 31);

c >> 31すべてのビットを元の MSB に設定します。これは、2 の補数で負の数の場合は 1、その他の場合は 0 です。

負数の右シフトは、C 標準ドキュメントに従って正式に実装定義されていますが、ほとんどの場合、MSB コピーで実装されます (一般的なプロセッサは単一の命令で実行できます)。

(c < 0)場合によってはブランチで実装される場合とは異なり、これによりブランチは確実になくなります。

于 2013-07-19T21:13:11.127 に答える
5

なんで支部が気になるの?[理由は質問へのコメントで説明されています。]

別の方法は次のようなものです。

((a - b) + 3600) % 3600

これは、すでに範囲内にあることを前提aとしています。それらが制御されていない場合、より一般的な解決策は、Drew McGowenが提案するものです。b0..3600

((a - b) % 3600 + 3600) % 3600

それだけ多くの計算を価値あるものにするためには、ブランチ ミスは非常に高くつく必要があります。

于 2013-07-19T15:29:09.980 に答える
4

@skjaidev は、分岐せずにそれを行う方法を示しました。ints が 2 の補数の場合に乗算を自動的に回避する方法は次のとおりです。

#if ((3600 & -0) == 0) && ((3600 & -1) == 3600)
c += 3600 & -(c < 0);
#else
c += 3600 * (c < 0);
#endif
于 2013-07-19T20:49:47.723 に答える
0

あなたがしたいのはモジュラー算術です。あなたの 2 の補数マシンは、すでに整数演算でこれを行っています。したがって、値を 2 の補数演算にマッピングすることで、modolo 操作を解放できます。

トリックは、角度を 0 から 1 イプシロンの間の 360 度の分数として表すことです。もちろん、一定の角度も同様に表現する必要がありますが、それは難しいことではありません。これは、変換関数 (er、マクロ) に隠すことができるちょっとした数学です。

このアイデアの価値は、角度を足したり引いたりすると、必要な分数部分と捨てたい整数部分を持つ値が得られるということです。分数を 2^32 の 2 進小数点を持つ 32 ビットの固定小数点数として表す場合 (たとえば、通常は符号ビットと見なされるものの左側)、分数のオーバーフローは単純に先頭から外れます。無料で32ビット値。したがって、すべての整数計算を行うと、「オーバーフロー」の除去が無料で行われます。

だから私はあなたのコードを書き直します(度×10の考え方を維持します):

  typedef unsigned int32 angle; // angle*3600/(2^32) represents degrees
  #define angle_scale_factor 1193046.47111111 // = 2^32/3600
  #define make_angle(degrees)  (unsigned int32)((degrees%3600)*angle_scale_factor ) 
  #define make_degrees(angle) (angle/(angle_scale_factor*10)) // produces float number

  ...

  angle a = make_angle(100);  // compiler presumably does compile-time math to compute 119304647 
  angle b = make_angle(200);  // = 238609294
  angle c = a - b; // compiler should generate integer subtract, which computes 4175662649

  #if 0 // no need for this at all; other solutions execute real code to do something here
  if (c < 0)  // this can't happen
     { c += 3600; } // this is the wrong representation for our variant
  #endif


  // speed doesn't matter here, we're doing output:
  printf("final angle %f4.2 = \n", make_degrees(c)); // should print 350.00

このコードをコンパイルして実行していません。

この角度を 100 倍または 1 倍にする変更は非常に簡単です。angle_scale_factor を変更します。16 ビット マシンを使用している場合、16 ビットへの切り替えも同様に簡単です。32 ビットがあり、それでも 16 ビット演算のみを実行したい場合は、出力する値を 16 ビットにマスクする必要があります。

このソリューションには、もう 1 つの優れたプロパティがあります。どの変数が角度であるかを文書化しました (そして面白い表現を持っています)。OP の元のコードでは単に int と呼ばれていましたが、それはそれらが表すものではありません。将来の保守担当者は、特に減算が変数から分離されていることに気付いた場合、元のコードに驚かれることでしょう。

于 2013-07-21T04:50:45.050 に答える