nekoponの日記: 10で割った剰余 12
日記 by
nekopon
割られる数をnとおきます
- 最下位ビットを別途取っておきます(n0 = n & 1)
- nを1ビット右シフト(n >>= 1)
- 残りを4ビットごとに区切って和を取ります
- n = ((0xf0f0f0f0 & n) >> 4) + (0x0f0f0f0f & n)
- n = (n >> 16) + n
- n = (n >> 8) + n
- n &= 0xf
- 残りが0-15になるのですが5で割った剰余を求めます
- 表引きでもよし (tmpres = 0x0432104321043210 >> (n << 2))
- if (((n >> 2) & 3) > (n & 3)) {
tmpres = 5 + (n & 3) - ((n >> 2) & 3);
} else {
tmpres = (n & 3) - ((n >> 2) & 3);
}
- 上記でfloor(n/2)を5で割ったあまりが求まるので、10で割ったあまりにするには
2倍してn0を足します (return (tmpres << 1) | n0)
これ5が22+1であるというところに鍵があったりします。
- x ≡ 1 (mod yz) のとき x ≡ 1 (mod y)
- x ≡ 1 (mod (x-1))
- x ≡ 1 (mod y) ならば x2 ≡ 1 (mod y)
なので
22k ≡ 1 (mod (22k) - 1)
22k ≡ 1 (mod (2k + 1))、ゆえに
24 (=16) ≡ 1 (mod 22 + 1 (=5))、さらに
24 x 2 ≡ 1 (mod 5)
ということで4ビットごとに区切って足していけば剰余が求まるというわけです。
もひとつ
- x ≡ -1 (mod x+1)
すなわち4 ≡ -1 (mod 5)なのが最後の if 文の表現であります。
剰余が出るだけで商は出ないので無能
1か所間違ってたのでこっそり修正~ (スコア:1)
ちゃんと検証してませんが (スコア:1)
乗算命令が遅くないCPUなら、愚直に商を求めてからその10倍の数を引いちゃうって手もあり?
素直に書けば
n1 = n * 0xcccccccd >> 35 << 3;
だが、
乗算の結果の上位32ビットだけ使うことを考慮して
n1 = n * 0xcccccccd >> 32 & 0xfffffff8;
こう書いてもいいかも?
n1 += n1 >> 2
すなわち、商の8倍+2倍=10倍の数が得られた。
result = n - n1;
すなわち、元の数から商の10倍の数が引かれたので、剰余が得られた。
Re:ちゃんと検証してませんが (スコア:1)
2桁までだし、Z80のコードですが、yasuoka氏のZ80のコード [srad.jp]だと、10で割った商と余りを求めるのに
最初は「10で割る(商)」→「10倍して引く(余り)」という流れで計算してましたが、
最終的には、
・まず10で割った余りを求める
・元の数から余りを引いて、10の倍数にする
・10で割って商を求める
って流れで計算してますね。
最後の10で割るとこは、「余りを引けば、得られる値は全て10の倍数になる。10=8+2をうまく使えば、商の計算は基本的にビット操作に落とせる」んだそうですが、Z80のコードみても、なぜそれで商になるのか私にはさっぱりわからない…
Re:ちゃんと検証してませんが (スコア:1)
yasuoka先生のコードはfloor(n/8)の8倍をBCDで得て(ADD A/DAA 3回はコレ)、n mod 8を足してもう一回BCDに補正するという座組です。10進補正命令があるからの技だと言わざるを得ません。
// 255/10から商=25、あまり=5が欲しいので足りませんでした
Re:ちゃんと検証してませんが (スコア:1)
Re: (スコア:0)
GCCで最適化オプションを付けたらこんな感じのコードになった。
やはり10で割って10倍して元の数から引くという方針のようだ
Re:ちゃんと検証してませんが (スコア:1)
Re: (スコア:0)
今どき乗算が遅いCPUはないので、こういうのはハードウェアロジックを作るときに使えるかなと思いました。
ここ20年くらいは遅くてもいい(小さな)乗算器(Z80でやったあの筆算方式)を手書きできる
設計者も少ない気がしますが、さすがに除算はHDLで合成できないのでやりたくないはずです。
ふつうに合成される乗算器はもっと速い後段の加算器のキャリーアップがないアルゴリズムを使うので、
加算器よりそれほど遅くありません。数倍いかない。でかいけど。
三本線等号 (スコア:0)
求めているものは、≡ U+2261 IDENTICAL TO ?
Re:三本線等号 (スコア:1)
Re: (スコア:0)
ごうどう
で変換できると思うっす
Re:三本線等号 (スコア:1)