基礎数学はプログラミング言語によってどのように効率的に評価されますか?


22

プログラミングの背後にある理論にますます関与していくにつれて、一見単純なことに魅了され、umb然とするようになります。

Q:これはどのように機能しますか?

Aそうだから!

私はこの実現が嫌いです!私は知識が大好きで、それに加えて学習も大好きです。それが私の質問につながります(それは広義の質問ですが)。

質問:

基本的な数学演算子はプログラミング言語でどのように評価されますか?

現在の方法はどのように改善されましたか?

var = 5 * 5; 

私の解釈:

$num1 = 5; $num2 = 5; $num3 = 0;
while ($num2 > 0) {
    $num3 = $num3 + $num1;
    $num2 = $num2 - 1;
}
echo $num3;

これは非常に効率が悪いようです。高い係数を使用すると、このメソッドは非常に遅くなりますが、標準の組み込みメソッドは瞬間的です。加算を繰り返さずに乗算をどのようにシミュレートしますか?

var = 5 / 5;

これはどのように行われますか?文字通り5を5つの等しい部分に分割する方法は考えられません。

var = 5 ^ 5; 

加算の反復の繰り返し?私の解釈:

$base = 5;
$mod = 5;
$num1 = $base;
while ($mod > 1) {

    $num2 = 5; $num3 = 0;
    while ($num2 > 0) {
        $num3 = $num3 + $num1;
        $num2 = $num2 - 1;
    }
    $num1 = $num3;
    $mod -=1;
}
echo $num3;

繰り返しますが、これは非常に非効率的ですが、これを行う別の方法を考えることはできません。この同じ質問は、自動的に処理されるすべての数学関連の関数にまで及びます。


1
ちょっとした裏話がありますが、私は大学でコンピューターサイエンスを学んでおり、後には数理理論や哲学や理論物理学も学んでいます。多くの願望、少しの時間。
コルビンシャント

10
en.wikipedia.org/wiki/Category:Computer_arithmeticからのすべてのリンクを見たことがあると想定しても安全ですか?
JBキング

2
基本的に、小学校で多桁の乗算と長い除算を行うように教えられたのと似ています。Aの1桁を取り、Bを掛けます。10をシフトします。Aの次の桁を取り、Bを掛けます。すべての桁について繰り返し、すべてを加算します。バイナリであるため、1桁の乗算はより単純(x0またはx1)であり、10シフトする代わりに2倍します。分割も同様です。
モニカについて質問する

回答:


35

コンピュータ内で算術がどのように機能するかを本当に理解するには、アセンブリ言語でプログラミングする必要があります。ワードサイズが小さく、乗算および除算命令がないものが望ましい。6502のようなもの。

6502では、実質的にすべての演算はアキュムレーターと呼ばれるレジスターで実行されます。(レジスタは、プロセッサ内の迅速なアクセスが可能な特別なメモリ位置です。)したがって、2つの数値を加算するには、最初の数値をアキュムレータにロードし、2番目の数値を加算します。

しかし、それは単純化しすぎです。6502は8ビットプロセッサであるため、0〜255の数字のみを処理できます。ほとんどの場合、より大きな数字で作業できるようになります。これらをまとめて、一度に8ビットずつ追加する必要があります。プロセッサには、2つの数値を加算した結果がアキュムレータをオーバーフローしたときに設定されるキャリーフラグがあります。プロセッサは、加算を行うときにそれを追加するため、数値の最下位バイトから開始すると仮定して、「1を運ぶ」ために使用できます。6502でのマルチバイトアドは次のようになります。

  1. キャリーフラグをクリア(CLC)
  2. 最初の数値の最下位バイトをロード(LDA、ロードアキュムレーター)
  3. 2番目の数値の最下位バイトを追加(ADC、キャリー付き追加)
  4. 結果の最下位バイトを保存(STA、ストアアキュムレータ)
  5. 上位バイトを連続してステップ2から4を繰り返します
  6. 最後にキャリーが設定されている場合、オーバーフローしています。エラーメッセージの生成などの適切なアクションを実行します(BCS / BCC、キャリーセット/クリアの場合は分岐)

減算は、キャリーを最初に設定し、ADCの代わりにSBC命令を使用し、最後にアンダーフローあればキャリーが明確になることを除いて、同様です。

ちょっと待って!負の数はどうですか?6502では、これらは2の補数と呼ばれる形式で保存されます。8ビットの数値を想定すると、-1は255として保存されます。これは、何かに255を追加すると、アキュムレータで1つ少なくなります(キャリーが加算されるため)。-2は254など、128まで格納される-128まで格納されます。したがって、符号付き整数の場合、バイトの0〜255の範囲の半分が正の数に使用され、半分が負の数に使用されます。(この規則を使用すると、数値の上位ビットをチェックして、負であるかどうかを確認できます。)

24時間時計のように考えてください。時間に23を追加すると、1時間早くなります(翌日)。したがって、23は-1に相当するクロックのモジュラーです。

1バイト以上を使用している場合、ネガにはより大きな数値を使用する必要があります。たとえば、16ビット整数の範囲は0〜65536です。したがって、65535は-1を表すために使用されます。これは、任意の数に65535を追加すると1つ少なくなります(キャリーが追加されるため)。

6502には、加算、減算、2の乗算(左シフト)、および2の除算(右シフト)の4つの算術演算しかありません。乗算と除算は、バイナリを扱うときにこれらの操作のみを使用して実行できます。たとえば、5(バイナリ101)と3(バイナリ11)の乗算を検討してください。10進数の長い乗算の場合と同様に、乗数の右桁から始めて101を1倍し、101 を求めます。次に被乗数を左シフトし、 1010を1倍して1010を求めます。 15.乗算するのは1または0だけなので、実際には乗算しません。乗数の各ビットは、単に(シフトされた)被乗数を追加するかどうかを示すフラグとして機能します。

除算は、バイナリを除き、試行除数を使用した手動の長い除算に似ています。定数で除算する場合、減算に似た方法でこれを行うことができます。Xで除算するのではなく、事前に計算された1 / Xのレンディションで乗算し、目的の結果とオーバーフローを生成します。今日でも、これは除算よりも高速です。

アセンブリで浮動小数点演算を実行するか、アセンブリで浮動小数点数を適切な出力形式に変換してみてください。そして、覚えておいてください、それは1979年であり、クロック速度は1 MHzですので、できるだけ効率的にそれをしなければなりません。

ワードサイズが大きくなり、レジスタが増えることを除いて、今日でもこのように動作します。もちろん、ほとんどの計算は現在ハードウェアで行われています。しかし、それはまだ同じ基本的な方法で行われています。たとえば、乗算に必要なシフト数と加算数を合計すると、実行された6809などの命令を持つ初期プロセッサのハードウェア乗算命令に必要なサイクル数とかなり相関します。マイクロコードでは、手動で行うのとほぼ同じ方法で。(トランジスタの予算が大きい場合、シフトと加算を行うためのより高速な方法があるため、最新のプロセッサはこれらの演算を連続して実行せず、わずか1サイクルで乗算を実行できます。)


3
ねえ、あなたの非常に詳細な説明をありがとう!それはまさに私が欲しかったものです!私のレベルでは、あなたをサポートしていることが一般的にあなたがやっていることよりも複雑であることを忘れます。それが、コンピューターサイエンスを勉強したい理由です。時間をさかのぼれば、世界が変わることは何もないこと、正しいSQLステートメントをどのように定式化するかを知っているだけだという事実が嫌いです。あなたは私が私が掘り下げようとしているものに私に味覚テスターを与えました。
コルビン・スザント

7
アセンブリ、反対するあなたは、ハードウェアを見ている、または少なくともハードウェアアルゴリズムどのようにコンピュータが行う算術演算を知りたい場合は、高すぎるまだある
JK。

えー 加算器とシフターがあることがわかったら、それらがソフトウェアと同じようにハードウェアで制御されることを想像するのは簡単で、ソフトウェアで遊ぶのは簡単です。
親切な

4
-1。ハードウェア乗算は、シフトでは行われていませんでしたが、30年近くにわたって追加されており、多くのCPUは1サイクルで乗算を実行できます。Binary Multiplier詳細については、Wikipediaの記事をご覧ください。
メイソンウィーラー

24

最終的に、基本的な算術演算はハードウェアで実行されます。より具体的には、CPU(または実際にはそのサブパート)

言い換えれば、それは電子回路です。適切なビットを入力として設定すると、適切なビットが出力として取得されます。これは、基本的な論理ゲートの組み合わせです。

http://en.wikipedia.org/wiki/Adder_%28electronics%29

http://en.wikipedia.org/wiki/Binary_multiplier


3
ハードウェアのアルゴリズムは慎重に指定されており、ハードウェアとは別に検討できます。
-S.ロット

@ S.Lott:あなたのコメントは紛らわしいと思う。私にとって、アルゴリズムには、あなたが従う一連のステップ、手順、あなたがプログラムできるものが含まれます。ここでは、基本的な算術演算を実行する電子回路について話しています。つまり、電流が流れるゲートのシーケンスだけです。したがって、せいぜい「アルゴリズム」よりも「論理的」です。...私の2セント。
-dagnelies

6
アルゴリズムは「有限、明確かつ効果的」です。回路内で実行することも、紙と鉛筆で実行することも、皿やDNAのティンカートイや分子で実行することもできます。アルゴリズムは何でもかまいません。電子回路は、定義されたアルゴリズムに従う必要があります。魔法のようにアルゴリズムの必要性を乗り越えることはありません。
S.Lott

1
1つのステップのみで構成されるプロセスは、「アルゴリズム」と見なされますか?FWIW、電子回路は一般に真理値表に従います-シングルステップ処理。真理値表が多層ゲートに「コンパイル」されるということは、それが単一ステップのプロセスであるという事実を否定するものではありません。
スリーブマン

2
@ S.Lott:より適切な最初のコメントは次のとおりです。ハードウェアの「ロジック」は慎重に指定されており、ハードウェアとは別に調査できます。そして確かにそうです。バイナリロジックの研究は、ブール代数と呼ばれます。
スリーブマン

6

これはすべて、Don KnuthのThe Art of Computer Programmingの徹底した忍耐でカバーされています。

加算、減算、乗算、除算の効率的なアルゴリズムはすべて詳細に説明されています。

分割をうまくカバーするこのようなものを読むことができます。

http://research.microsoft.com/pubs/151917/divmodnote.pdf


5

電子回路によってピコ秒単位で行われます。詳細については、Googleの「ハードウェア乗数」など。最新のCPUは、何十年にもわたって継続的に改善されてきた非常に複雑な結果です。

ところで、あなたは繰り返し足し算を掛けないので、なぜあなたはコンピュータがそれを想像するでしょうか?


私の質問は、関数自体ではなく、関数の背後にある理由に関するものです。プロセッサによって解釈されることを理解しています。具体的には、その背後にある理論と、擬似コードでどのように複製できるか。
コルビンシャント

1
私の頭の中での掛け算は記憶です。また、長い乗算では、私が行った方法での反復が必要です。私は先に行き、長い乗算のための関数を一緒に投げます
コルビン・スザント

2
@Korvin、これがあなたの興味であれば、私が推薦した本はあなたに役立つでしょう。また、Harold AbelsonとGerald Jay Sussmanによる「コンピュータープログラムの構造と解釈」もお勧めします。これらの質問を詳細に扱います。
ジョナサンヘンソン

いくつかの初期のコンピューターは、加算と減算のみをサポートしていました。一部は減算のみをサポートしました!したがって、操作x = y * zはdo(z times){x + y}として実装され、同様に除算x = y / zはwhile(y> z){x + 1; y = y-z}
ジェームズアンダーソン

@James:シフトをサポートしましたか?乗算はシフトと加算を介して行われ、除算はシフト、比較、減算で行われると予想されます。
ケビンクライン

4

これは決して完全な回答を意味するものではありませんが、どのように実装されているかをある程度理解できるはずです。おそらくご存知のように、数字はバイナリで表されます。たとえば、コンピューターは数値5を00000101として表すことができます。コンピューターで実行できる非常に基本的な操作は、10進数の10である00001010を与える左シフトです。数字を左に1回シフトするたびに、数字を2倍にします。数値xがあり、それを17倍したかったとします。xを左に4回シフトし、結果にxを追加できます(16x + x = 17x)。これは、数字に17を掛ける効率的な方法です。これにより、繰り返し加算を使用することなく、コンピューターがどのように大きな数字を掛けることができるかがわかります。

除算では、加算、減算、右シフト、左シフトなどの組み合わせを使用できます。また、数値を指数に上げるための多くのトリックがあります。


明確にするために、通常は一度に1ビット以上シフトできます。したがって、これらの4つのシフト操作は、実際には次のような1つの操作ですshl r0, 4
カレブ

4

私は子供の頃、ペンと紙で掛け算と割り算をする方法を学びました。後で、平方根もそのように計算できることを知りました。

大学では、十数回の乗算、除算、加算で三角法と対数演算を計算する方法を学びました。彼らはそれをテイラー級数と呼びました。

その前に、父は私にそれらの複雑な操作がすでに数百の値に対して計算され、表で提示されている本をくれました。また、2つの計算された値の間の値のサインが必要な場合、エラーを推定するためのいくつかの説明がありました。

整数ユニット、浮動小数点ユニット、GPU、およびDSPは、これらの古い技術をすべてシリコンに実装しています。


3

あなたが提起する問題を使用して、デジタル処理の問題を解決するためにデジタル回路がどのように設計されているか、CPUがどのように加算と乗算を実装するかを考えてみましょう。

まず、直接的な質問を邪魔にならないようにしましょう。プログラミング言語はどのように乗算と加算を効率的に評価するのでしょうか。答えは簡単で、それらをコンパイルして乗算命令と加算命令にコンパイルします。たとえば、次のコード:

a = 1 + 1;
b = a * 20;

以下のように単純にコンパイルされます:

ADD 1 1  a
MUL a 20 b

(上記のアセンブリは、単純化のために、存在しない架空のCPU用です)。

この時点で、上記の答えは単に問題をシフトし、ハードウェアマジックによってそれを解決することがわかります。次の質問は、明らかに、そのハードウェアの魔法はどのように機能するのでしょうか?

最初に、より単純な問題である加算を見てみましょう。

まず、おなじみの問題を行い、通常の10進数を追加します。

 17
+28

最初のステップは7と8を追加することです。しかし、これは1桁以上の15になります。だから私たちは1を運びます:

(1)
 17
+28
= 5

次に、1、1、2を一緒に追加します。

 17
+28
=45

したがって、これから次のルールを取得します。

  1. 加算の結果が複数桁の場合、最下位桁を保持し、最上位桁を繰り上げます

  2. 列に数字が繰り越されている場合は、追加する数字とともに追加します

ここで、上記のルールを基数2(ブール代数)で解釈します。

したがって、ブール代数では、0と1を一緒に追加する=1。0と0 = 0を追加します。1と1 = 10を追加します。

これから、真理値表を作成できます。

a b  |  sum  carry
-------------------
0 0  |   0     0
0 1  |   1     0
1 0  |   1     0
1 1  |   0     1

これから、2つの回路/ブール方程式を構築できます。1つは合計の出力用で、もう1つはキャリーの出力用です。最も単純な方法は、すべての入力を単純にリストすることです。どんなに大きくて複雑でも、この形式で言い換えることができる真理値表:

(AND inputs in first row) OR (AND of inputs in second row) OR ...

これは基本的に製品の合計フォームです。結果が1になり、0を無視する出力のみを確認します。

sum = (NOT a AND b) OR (a AND NOT b)

AND ORおよびNOTをプログラミング言語のシンボルに置き換えて、読みやすくします。

sum = (!a & b) | (a & !b)

基本的に、テーブルを次のように変換しました。

a b  |  sum  equation
-------------------
0 0  |   0   
0 1  |   1   (!a & b)
1 0  |   1   (a & !b)
1 1  |   0   

これは、回路として直接実装できます。

                _____
 a ------------|     |
    \          | AND |-.     ____
     \  ,-NOT--|_____|  \   |    |
      \/                 `--| OR |----- sum
      /\        _____    ,--|____|
     /  `-NOT--|     |  /
    /          | AND |-`
 b ------------|_____|

観察者は、この時点で、上記のロジックが実際に単一のゲートとして実装できることに気付くでしょう-真理値表に必要な動作を便利に行うXORゲート:

                _____
 a ------------|     |
               | XOR |---- sum
 b ------------|_____|

ただし、ハードウェアがXORゲートを提供しない場合、上記の手順は、AND、OR、およびNOTゲートの観点からそれを定義および実装する方法です。

論理ゲートを実際のハードウェアに変換する方法は、使用しているハードウェアによって異なります。メカニズムが何らかのスイッチング動作を提供する限り、さまざまな物理メカニズムを使用して実装できます。論理ゲートは、水や空気の噴出(流体)からトランジスタ(電子工学)、落下する大理石まで、あらゆるもので実装されています。それはそれ自体が大きなトピックなので、私はそれを大雑把に言い、論理ゲートを物理デバイスとして実装することが可能であると言うつもりです。

キャリー信号についても同じことを行います。キャリー信号が真になる条件は1つしかないため、式は単純です。

carry = a & b

キャリーは簡単です:

                _____
 a ------------|     |
               | AND |---- carry
 b ------------|_____|

それらを組み合わせると、半加算器と呼ばれるものが得られます。

                _____
 a ------;-----|     |
         |     | XOR |---- sum
 b --;---|-----|_____|
     |   |      _____
     |   '-----|     |
     |         | AND |---- carry
     '---------|_____|

ところで、上記の回路の方程式は次のようになります。

sum = a ^ b
carry = a & b

半加算器に何かがありません。結果が繰り越しよりも1桁以上の場合、最初のルールを実装しましたが、2番目のルールは実装していません-キャリーが存在する場合、それを数字と加算します。

したがって、1桁以上の数値を加算できる加算回路である全加算器を実装するには、真理値表を定義する必要があります。

a b c  |  sum  carry
---------------------
0 0 0  |   0     0
0 0 1  |   1     0
0 1 0  |   1     0
0 1 1  |   0     1
1 0 0  |   1     0
1 0 1  |   0     1
1 1 0  |   0     1
1 1 1  |   1     1

合計の式は次のとおりです。

sum = (!a & !b & c) | (!a & b & !c) | (a & !b & !c) | (a & b & c)

上記のように、同じプロセスを経て方程式を因数分解して単純化し、回路などとして解釈することもできますが、この答えは長すぎると思います。

ここまでで、デジタルロジックがどのように設計されているかがわかるはずです。Karnaughマップ(真理値表を単純化するために使用)やespressoなどの論理コンパイラー(ブール式を手動で因数分解する必要がない)など、私が言及していない他のトリックもありますが、基本は基本的に上記の概要:

  1. 1ビット(桁)レベルで作業できるようになるまで、問題を分解します。

  2. 真理値表を使用して、必要な出力を定義します。

  3. テーブルをブール方程式に変換し、方程式を単純化します。

  4. 方程式を論理ゲートとして解釈します。

  5. 論理ゲートを実装して、論理回路を実際のハードウェア回路に変換します。

これが、根本的な(または、低レベルの)問題が実際に解決される方法です-たくさんの真理値表。実際の創造的な作業は、MP3デコードなどの複雑なタスクをビットレベルに分解して、真理値表で作業できるようにすることです。

申し訳ありませんが、乗算を実装する方法を説明する時間がありません。乗算の動作時間のルールを考え、それをバイナリで解釈し、それを真理値表に分解してみることで、それに挑戦することができます。または、Wikipediaを読むことができます:http : //en.wikipedia.org/wiki/Binary_multiplier


2

基本的な算術命令は、非常に効率的なアセンブリ命令で実行されます。

より複雑な(または抽象的な)命令は、ループメカニズムを使用してアセンブリで実行されるか、std libsで処理されます。

大学で数学を勉強すると、ラムダ計算やBig-O表記などの勉強を始めます。これらのすべて、およびそれ以上は、効率的なアルゴリズムを評価および作成するためにプログラマーによって使用されます。とにかく、基本的なことは通常、アセンブリやcでのポインタなどの低レベルで行われます。

このトピックの優れた紹介は、Charles Petzoldによる「コード」です。


1
またはルックアップテーブル。値を事前計算して検索するのがはるかに迅速になりました。例Sin / Cos / Tan(整数除算ですが、事前に計算され、ハードウェアに保存されます)。
マーティンヨーク

1

などの著書を取得ディジタルロジックの基礎...私はまだ新入生/年生EEの学生のためにかなりの標準だと思うと、それ(EDはを通してあなたの方法を動作します。それは、大金がかかる使用または以前の版のために見てそうそれ)。これにより、加算器と乗算器について学習し、ハードウェアが何をしているのかの原理を理解するのに十分な背景知識が得られます。

あなたの答えは、短期的には、「そうするから」ではなく、「この複雑な振る舞いを喚起するために、より単純なロジックの無数の部分を組み合わせるから」になります。

プログラムがどのようにコンパイルされ実行されるかの原則をすべて理解しようとする場合は、一緒に進めてください。そうすれば、最終的にすべてがどのように一致するかを確認できます。


1

ここにはたくさんの良い答えがあります。あなたも正しい考えから始めました。乗算のような複雑な演算は、より単純な演算から構築されます。ご想像のとおり、一連の加算を使用するよりも、乗算命令を使用せずに乗算する方法がより高速です。乗算は、より小さい乗算の合計として、またはシフトと加算の組み合わせとして実装できます。例:

a = 5 + 5 + 5 + 5 + 5;           // 5*5, but takes 5 operations
b = (5 << 2) + 5;                // 5*5 in only 2 operations
c = (41 << 4) + (41 << 2) + 41   // 41*21 in 4 operations

分割も同様に小さな操作に分割できます。XOR(^)は、これまで見てきたすべてのプロセッサに組み込まれた命令ですが、それでも、AND、OR、およびNOTの組み合わせとして実装できます。

ただし、プロセッサが提供する命令の種類や、それらの命令をどのように組み合わせてより複雑な操作を行うかについての一般的な考えよりも、特定の答えはあなたにとって満足のいくものではないと感じています。その種の好奇心にとって、アセンブリ言語を健全に使うことほど良いものはありません。MIPSアセンブリ言語の非常に親しみやすい紹介です。


1

最新のプロセッサが2つの64ビット整数の乗算を実装する方法は次のとおりです。

ロングハンド乗算を行う方法を知っています。2つの10桁の数字を乗算するには、1つの10桁の数字に他の数字の各10桁を乗算し、11桁の結果を上下に並べてシフトし、すべての数字を加算します。

最新のプロセッサは、すべての64 x 64ビットでこれを行います。ただし、2つの単一ビット数の乗算は非常に簡単です。1x 1 = 1、他のすべての積はゼロです。これは論理ANDで実装されます。また、結果が2桁になることがある10進積とは異なり、単一ビット数の2進積は常に1ビットです。

したがって、64ビットの64行を追加する必要があります。しかし、64ビット数の64の追加は遅いです。そのため、プロセッサは3/2加算器または7/3加算器のいずれかを使用します。3つの単一ビット数を加算すると、結果は0、1、2、または3になり、2ビットに収まります。7つの単一ビット数を追加すると、結果は0〜7の数になり、3ビットで表すことができます。IBMは、わずか18のプリミティブ回路(PowerPCのドキュメント)で7/3加算器を作成できると主張していますが、IntelとARMも同様にできると思います。

4096ビットがあり、同じビット位置で7ビットの約600グループにグループ化し、約600 7/3加算器を使用して結果を4096ビットから2,000未満に減らします。次に、通常の全加算器に入力できるビットのペアで終わるまで、同じことを何度も繰り返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.