これはもっとエレガントな方法ですが、ここでは始めるには十分です。
通常、このような除算は、逆数を掛けること、すなわち、最初に乗算してから右にシフトすることによって行われる。
は(注:例えばn * 3 = (n*2) + (n*1) = (n << 1) + (n))
(つまりmultplicationがシフトすることにより達成して追加することができます覚えているが、私はちょうどここに乗算を使用するつもりだあなたの質問は、「シフト&を追加」と私は)乗算の私の速記使用を正当化していました。以下の例で
、私は一例で概念を説明しようとしています。あなたの特定のケースでは、あなたがそのような(私は符号なしint型を使用してい
記号などの問題を検討したいと思います以下)
オーバーフロー(以下では、中間値を保持するために32ビットの符号なしlongを使用していますが、小さいuCを使用している場合はそれに応じて調整してください)
9/5は1または2を返しますか?
(使用可能なビット数)できるだけ、除算の前にすべての乗算を行います(切り捨てエラーを最小限に抑えるために)、Cで1を返します。 。再度、オーバーフローに注意してください。
私が言ったように、以下を読んでコンセプトを理解し、必要に合わせて調整します。
192で除算することは1/192を乗算することと同じですが、(64 * 3)で除算することと同じです。1/3の正確な(有限の)バイナリ表現はないので、0x5555 /(1 < < 16)で近似しています。 192によって分割する
、我々は64で除算し、次いで3により分割する3による除算、我々は0x5555で掛けると16によって右シフト(または0x55を掛けると>> 8、又は...)
// 8000/192 =
// ((8000/64)/3) =
// ((8000 >> 6)/3) =
// (((8000 >> 6) * 0x5555) >> 16)
// (((8000 * 0x5555) >> 22
括弧は意図的なものです。あなた(8000 * (0x5555/(1 << 16))
2番目の項が0で、製品が0になるため計算したくありません。
ので、コード内で1ライナーは、のようになります。これは、「C」は42が「近い」であっても、8000/192
ために生じるであろうものである、41が得られます
printf("Answer: %lu\n", ((8000UL * 0x5555UL) >> 22));
。 LSBをチェックすることで、必要に応じて丸めることができます。
このトピックについては論文を書くことができますが、幸いにもsomeone much smarter than me already hasです。
出典
2011-02-03 20:24:51
Dan
さらに反射に答えは明らかである.... 192分の1 = 5.208x10^-3存在/ 2^8 + 1/2^10 + 1/2^12 ...となり、十分な精度に達するまで項が追加されます。 – trican
単なる注意:これは、最適化コンパイラが最適なアーキテクチャの場合、与えられた除数に対して完全最適化を使用してコンパイルを行い、出力を調べることができます。現代の最適化コンパイラは、与えられた計算でどんな種類の数学/ビット操作(もしあれば)が最速で実行されるかを決定する上で非常に優れています。 –
これは、数値の固定小数点表現による乗算と同等です。これは、実際の除算と同じ丸め動作をすることはないので、最適化コンパイラはおそらくそれをしません。 – phkahler