すべてのCPUで、除算操作は比較的遅く、何もできません。 ただし、除数が定数の場合、除算は他の定数(コンパイル時に計算される逆数)による乗算に置き換えることができます。 その後、コードは少し速く動作します(そして消費電力が少なくなります)。 多くのコンパイラ(gcc、MSVC)がこの最適化を行いますが、多くの開発者は係数の計算方法を知らず、これは簡単ではないことがわかります。
係数の計算方法についてさらに説明します。
実数のアイデアとケース
たとえば、コードが
double x = a / 2.5の場合、これは
x = a *(1.0 / 2.5)または
x = a * 0.4に置き換えることができます。 コードは高速になりますが、精度が低下する場合があります。 整数の場合、額にこのようなトリックは機能しません。 係数は1(ゼロ)より小さくなります。 固定小数点数(仮数
N )を使用できます:
int x = a / b = a * B >> N、ここでB =(1 << N)/ b 問題は、計算されたxが正解とは1異なることがあり、整数では受け入れられないことです。
アルゴリズム
単純化するために、非負の数のみを使用します。 数学的定式化:
既知の整数
bについて、すべての整数
a に対して0≤a <(1 << N ) 、
x =
[a / b] = [a * B >> M]である整数のペア
M、Bを見つけます。ここで[]は整数です
そして、>> Mは、
aを2の
M乗で割る(および<<乗算する)ことです。
1.0 / bをバイナリ形式(場合によっては無限に長い数)で記述します。最初の
Mビットは整数
Cとして示され、残りのビット(尾部余り)は
Dとして、
1.0 / b = C >> M + D、D <(1> > M) 。
B = Cかつ
a * D≤1 (
N≤M )の場合に何が起こるか見てみましょう:
x = [a / b] = [a *(C >> M + D)] = [a * C >> M + a * D]≥[a * C >> M] + [a * D] = [ a * C >> M]Bが数値
1 / bの最初の
Mビットであり、残りのビットが破棄される(ゼロに置き換えられる)場合、正しい値が得られることがあり、
xよりも1少ないことがあります。 これは
、* Dが破棄されるために発生します。これは、
1未満ではありますが、全体を変更する可能性があります。
Bを1増やしてみて(
B = C + 1 )、それから:
[a *(C + 1)>> M] = [a * C >> M + a *(1 >> M)]≥[a * C >> M + a * D] = [a *(C> > M + D)] = [a / b] = xここで、正しい答え
xが得られる場合と、さらに1が得られる場合があります。
乗算によって
xを正確に計算することは不可能に思われるかもしれません。なぜなら、ある近似では、数値が必要な数よりも1少なくなり、さらに1になるためです。 しかし、これはそうではありません。 実際、
aは整数であり、最大小数部
a / bは正確に
(b-1)/ bであり、
xの計算では
a *((1 >> M)-D)増加します。 ええ、それは
a *((1 >> M)-D)<1 / bの場合、不平等が平等に変わることを意味します!!! 数
Lを 、
(1 << L)≥b 、次に
(1 >> M)≤(1 >>(L + N))または
M≥L + Nで同等になるようにします。
Javaの例
この例では、除数は127(
L = 7)です。 JVMによる乗算の結果は32ビットに収まるはずです
。N =(32-7)/ 2 = 12を選択します。例:
final static int N = 12; private static int divideBy127(int a) { final int b = 127; final int L = 7; final int B = ((1 << (N + L)) / b) + 1; return (a * B) >>> (N + L); } public static void main(String[] args) { final int size = 20000; final int times = 10001; final int[] data = new int[size]; for (int i = 0; i < size; ++i) data[i] = (i * i) & ((1 << N) - 1);
Intel Core-i5 2500、JITおよびオプション-XX:+ AggressiveOptsを使用したJVM 1.7.0u5では、除算テストは乗算の
2倍遅くなります 。
追加
この記事で取り上げていないトピックの残りの部分:
- 配当はマイナスになる場合があります。
- 乗算中のオーバーフローを回避する方法。
- 整数にx86を乗算した結果は64ビットを必要とし、特定の条件下では、右へのシフトを削除できます。
- 数値が割り切れる整数であることがわかっている場合は、別のアルゴリズムを使用できます。
- 場合によっては、乗算を左シフトと加算で置き換えることができます。
- 実行速度がさらに速い一部の仕切りの特殊なケース。
文学
「乗算を使用した不変整数による除算」、Torbjorn Granlund、Peter L. Montgomery-正式な証明およびトピックに関するその他のアルゴリズム。