|
/*========================================================*/ <<<あゆしゃのC言語プログラミング>>> /*========================================================*/ 第432回 掛け算割り算アルゴリズム 発行 2004年3月8日(月曜日) 発行数 約3100 {magclick} /*========================================================*/ はじめに ( 決り文句 ) /*========================================================*/ ・このメールマガジンはまぐまぐさんから発行しています。 ・ジャンルは、マルチメディアのプログラム、C言語です。 ・このメールマガジンは、横60文字で作成しています。 また、インデントはすべて半角スペース4つで構成しています。 ・ここで扱うプログラムは、C言語と半光年以内のものです。 ・登録解除は、まぐまぐさんのホームページでお願いします。 ・まぐまぐさんのバックナンバー(下欄参照)を活用して下さい。 ・ここは私の復習の場です。内容は約1ヶ月内外に私が勉強した 内容になっています。最新の技術があれば、へたれもあります。 ・わかりやすさを優先させる為、たまに嘘があるかもしれません。 /*========================================================*/ ご挨拶 /*========================================================*/ こんにちは。あゆしゃです。 先日、私としては極めて珍しいことに、車を拭きました。 雨にぬれた車体をタオルで拭いただけですが。 ところがそれがいけなかったのか、その後、こんな季節なのに、 ★真昼の3時、豊田に雪が降りました。 ・・・私のせい? {magclick} /*========================================================*/ 今回のお題 << 掛け算割り算アルゴリズム >> /*========================================================*/ の前に、前回の内容に2点間違いがありました。 1つは、40クロックなどと紹介したところ、単位がクロック ではなくてマイクロ秒でした。 もう1点は、割り算を使った処理も速度は同じ、と紹介した ところ、同じではなく、4倍の差がありました。 もちろん、割り算なんざを使ったほうが、4倍遅いのです。 ちなみに、計測した割り算を使ったほうの処理は、こんな感じ です。 UINT s = 1, x = a; if( a == 0 ) return 0; while( s < x ) { s <<= 1, x >>= 1; } // xの桁数半減 do { x = s; s = ( a / s + s ) / 2; } while( s < x ); // 誤差がある間ループ return x; 桁数半減、というところは、前回お話したポイント通りで、 桁数を半分にすることで、平方根の近似値を求めているのです。 その後のループでそれを正解に近づけている、というわけです。 正解になるとsとxの値が一致します。WHILEはそれを待ちます。 これが大体280マイクロ秒でした。 ちなみにCPUは2メガヘルツ、1クロックあたり 0.000488マイクロ秒 になります。1/2048です。 ちなみに1ヘルツだと、1クロック1秒です。 /*========================================================*/ さて本題、掛け算と割り算のアルゴリズムについて。 これがあると、32ビットをサポートしていない旧式CPUで 32ビット掛け算割り算をサポートできます。 また、巨大な桁数をサポートすることもできるでしょう。 当たり前すぎることなので、アルゴリズムを調べても、 この手のものは見付けられませんでした。 (あったら教えてください。。。) よって、比較するものがありません。 ここに紹介するのは、まぁ平方根もそうなのですが、あゆしゃ流 のアルゴリズムのみです。 ミスがあったら、ごめんなさいね? /*========================================================*/ さて、掛け算。 掛け算は、単純に足し算の連続です。 しかし、馬鹿正直に1つずつ足して行っては、日が暮れます。 よって、ビットのスケールにあわして、足していきます。 つまり、*257ならば、+*1+*256、を計算します。 *256だったら、シフトで求めることができますよね。 UINT mulul( UINT a, UINT b ); UINT ret = 0; int i = 32; while( --i >= 0 ) { if( b & 1 ) ret += a; b >>= 1, a <<= 1; } return ret; まぁ、こんな感じですか。 retは最初は0で、かける数(b)のビットが足っていたら、 元の数(a)をretに足し込みます。 そして1回ごとに、aとbのスケールを変更します。 bについては&1でビット判定をするためにシフトします。 アセンブラをつかってシフトしてキャリーに値を拾うほうがよい のですが、まぁ、いいでしょう。 /*========================================================*/ 続いて、割り算。 割り算は、単純に引き算の連続です。 しかし、馬鹿正直に1つずつ引いて行っては、日が暮れます。 よって、ビットのスケールにあわして、引いていきます。 つまり、/257ならば、-*256-*1、を計算します。 *256だったら、シフトで求めることができますよね。 ただし、大きいほうから引いていくのがポイントです。 UINT divul( UINT a, UINT b ); UINT ret = 0; UINT mask = 1; int i = 32 - 1; while( --i >= 0 && a > b ) b <<= 1, mask <<= 1; while( mask ) { if( a > b ) ret += mask, a -= b; b >>= 1, mask >>= 1; } return ret; まぁ、こんな感じですか。 最初のループで、初めてaを上回るbを求めています。 ついでにそのときのビット位置をmaskに求めています。 次のループで、引けるのであれば引く、ということを繰り返し ます。 引くときに、その位置に相当するmaskをretに足すことで、 割り算の商を求めています。 足すのではなく、論理ORでもOKです。そっちのほうが早いかな。 意外に、簡単です。 /*========================================================*/ ちなみに、掛け算や割り算のアルゴリズムの中で、スケールの 小さい掛け算や割り算を使うのは、反則です。 ・・・シフトはいいのです、シフトだから。。。 {magclick} /*========================================================*/ さいごに /*========================================================*/ あんまし動作確認していません。時間がなくて、というのは いいわけですが。 平方根についてはループをまわして1億パターンぐらい やりましたが、今回は手入力で2,3パターン・・・? 駄目ですか? {magclick} /*========================================================*/ 次回予告 /*========================================================*/ 次回は3月15日(月曜日)に、第433回を送ります。 お題は「。。。」 あれが降臨中ですので、周一ペーストさせていただきます。 お題は・・・、えー、えー、困ったな。頭があれでいっぱいで。 何か話題を考えておきますゆえ。 お楽しみに! /*========================================================*/ 最後の決り文句 /*========================================================*/ このメールマガジンは、まぐまぐさんから発行しています。 このメールマガジンを解除したい場合は、まぐまぐさんをご利用 ください。このメルマガのまぐまぐアイディーは最後にあります。 このメールマガジンには広告が挿入されていますか? このメールマガジンの内容に文面の引用はありませんか? めーらっくすの場合はめーらっくすの利用方に従ってください。 このメールマガジンの内容の、転用、流用、宣伝、リンク、 レッスンしませんか? なんて大歓迎です。 {magclick} /*========================================================*/ /*========================================================*/ 発行者 あゆしゃ まぐまぐアイディー 0000020674 まぐまぐバックナンバー http://jazz.tegami.com/backnumber/frame.cgi?id=0000020674 あゆしゃの世界 http://ayusya.hp.infoseek.co.jp/ 登録と解除 http://www.mag2.com/m/0000020674.htm ご意見・ご感想・ご質問メール mailto:ayusya@flamenco.plala.or.jp めーらっくす <<過去ログがタイトル別になっています>> http://www.mailux.com/mm_dsp.php?mm_id=MM3E1AEE285AB4F |