レス数が1スレッドの最大レス数(1000件)を超えています。残念ながら投稿することができません。
おちゃめくらぶ掲示板
-
固定小数点と浮動小数点はどちらがいいのか?
プチコンでは浮動小数点ではなく固定小数点が採用されているにょ。
一見すると扱える範囲が広そうな浮動小数点の方がいいのだけど果たして本当にそうなのか?
まずは次の式を見て欲しいにょ。(N88BASICなど多くのBASICで動作するはず)
A=(A+1) mod 4
※modは剰余を返す演算子とする
これは、どのような動作をするのかというとすごく簡単でAの値は0、1、2、3、0、・・・を
繰り返すにょ。
つまり、0から特定の値-1まで繰り返すカウンタが欲しい場合はこれで簡単に実装できるにょ。
プチコンにおいては剰余を返す演算子はC言語と同じ%だけどプチコンがC言語や他の多くの
BASIC言語と異なる点は剰余の演算にで小数を含む値を使用できるという点にょ。(剰余で
小数が扱えるため2月1日に書いたようなことも可能になる)
それをふまえて次の式を見て欲しいにょ。
A=(A+0.25)%1
プチコンで実行したことがない人には分かりにくいけどこれは最初に書いたものを単純に4で
割っただけなのでAの初期値が0ならば0、0.25、0.5、0.75、、0、・・・を繰り返すことは
予想ができると思うにょ。
実際にループで実行して表示を繰り返してみれば分かるけど初期値がどのような数であっても
ループ4回で初期値に戻るにょ。
では、Aに加算するのが0.25ではなく0.1だったらどうなるのか・・・?
A=(A+0.1)%1
これをプチコンで実行すればループ10回で初期値に戻るように見えると思うにょ。
しかし、実際はそうならないにょ。
というのも0.1には固定小数点による誤差があるからにょ。
プチコンは32bit固定小数点であり、符号1bit、整数19bit、小数12bitとなっているにょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/p005.htm#column
つまり、0.1という定数はプチコン内部では409/4096という形になっているわけにょ。
それを踏まえて考えるとA=(A+0.1)%1というのはA=(A+409)%4096というものを4096で割ったもの
といえるにょ。
そう考えるとAの初期値が0ならば10回ループした後ではAの値は4090/4096でありちょうど1に
なっていないためループ10回では初期値に戻らないというのが分かると思うにょ。
それではループ何回で初期値に戻るのかを考えてみるにょ。
初期値に戻るためには409を一定倍して4096の倍数になるかを求めればいいのだけど409は
素数であり、4096=2^12で互いに素であるため409n=4096k(n、kはともに整数)が成立する
最小のnは4096となるにょ。
つまり、A=(A+0.1)%1という式はループ4096回で初期値に戻るというわけにょ。
これが何の役に立つのかというと難しいところだけど以前作った疑似乱数発生ルーチンで
有用になってくるにょ。
0〜1の疑似乱数を発生させるルーチンはY=(Y+1)%4095X=(X*479232+Y)%4096/4096というように
なっていたにょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/rnd.htm
X=(X*479232+10)%4096/4096という式だと周期は4096しか確保できないにょ。
これだとあっと言う間に同じ値を繰り返すため256x192ドットの画面をランダムに表示された
ドットで埋めるなんてことは不可能であり実用性は低くなってしまうにょ。
したがって、別途変数Yによるカウンタを用いて周期を長くしたものが上記の疑似乱数にょ。
変数Yは4095回で元に戻るため4096回周期のXとは異なる周期であり、4096と4095は互いに素で
あるため周期は4096×4095=16773120に達するにょ。
ここで変数Yに注目すると単に長い周期を繰り返せばY=(Y+1)%4095に拘る必要はないにょ。
A=(A+0.1)%1でAが4096回で初期値に戻るというのを利用してこれをカウンタに使えばいいの
だけど周期4096ではXと周期が同じであるため意味がないにょ。
したがって、4096とは素となる数(2の倍数ではない数)を4096で割ったものを設定する
必要があるにょ。
別に何でもいいのだけどここでは1.1(4096倍すれば4505)にしてみるにょ。
Y=(Y+0.1)%1.1でYは4505回周期となり、4096回周期のXと組み合わせると周期は18452480と
なるにょ。
そうすれば1.1ではなくもっと大きな数に設定すればさらに周期が伸びると考えるけど
周期が伸びてもXの取る値に変化はないため1より極端に大きな値を指定しても意味はないため
リスト短縮を考えて1.1がベターだと考えたにょ。(ちなみに4095回周期にしたければ0.9998に
すればよい)
出力するXの値が0〜1の範囲内であればXの方もX=(X*479232+Y)%4096/4096から短くできるため
次のようになるにょ。
Y=(Y+0.1)%1.1X=(X*117+Y)%1
http://ww5.tiki.ne.jp/~ochame/petitcom/tips/routine.htm#rand
0〜1の値を返す疑似乱数は固定小数点の誤差を上手く活用することで10文字分短縮できたと
いうことが分かるにょ。
プチコンの固定小数点は一見すると簡単に誤差が出るため非常に使い勝手が悪いような気が
するけど浮動小数点とは異なり加減算で新たな誤差が生まれないというのは大きなメリット
だと思うにょ。
それがあるからこそ上記のようなことが可能になったのだからね。
固定小数点は内部に保存する段階で誤差が発生するというだけにょ。
32bit浮動小数点(float)では符号1bit、指数部8bit、仮数部23bitとなっているにょ。
そのためfloatでは絶対値が1.175494×10^-38〜3.402823×10^38の数を扱うことができるにょ。
こうしてみると同じ32bitでもプチコンの固定小数点よりも優れているように思えるかも
しれないけど問題なのは仮数部が23bitしかないということにょ。
23bitで扱える数は1〜8388607(=2^23-1)となるにょ。
このため非常に大きい数と非常に小さい数を加算すると小さい数はちゃんと加算されないにょ。
これがプチコンの固定小数点の場合は525288以上の大きい数は事前に2のべき乗で割れば誤差
無しで加算できるにょ。
この方法によってプチコンで32bit整数演算は擬似的に可能となるにょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/tips/routine.htm#int
小さい数字は整数倍することでこちらも誤差無しで加算可能にょ。
何せ整数部と小数部で31bitあるためfloatよりも事実上扱える範囲は大きいにょ。
floatがさらにやっかいなのは減算にょ。
桁落ちによって誤差が拡大されてしまうからね。
固定小数点は小数点を含む数を表記すると(小数部分が1/4096単位で正確に表せる数を除き)
表記の段階で誤差が発生してしまうものの加減算では整数演算と同じように演算誤差が発生
しないにょ。
そのため誤差を減らすさまざまな方法を工夫すれば誤差無しで演算可能だけどfloatは確実に
誤差が出てしまうため誤差が発生しても問題ないようなコードを書く必要があるにょ。
もしも32bit浮動小数点のみだと初心者向けではなく玄人向けになってしまうにょ。
そう考えると変数の型を選べないプチコンで固定小数点となったのは納得がいくにょ。
整数演算だと誤差の心配はないけど小数が扱えないとさすがに不便だからね。
ポケコンの場合はシャープのポケコンは8byteBCD演算という極めて精度の高いものを搭載
しているにょ。
そして、PC-1280およびPC-E500シリーズでは倍精度演算(有効桁数20桁)が可能になって
いるにょ。
BCD演算は内部で10進数演算が可能であり、誤差は極めて小さいし、さらに有効数字で20桁の
倍精度となるとこれに適うBASICを搭載したパソコンは存在しないにょ。
まさに電卓の進化系であるポケコンならではといえるにょ。
ここまでくると固定小数点より圧倒的に扱いやすいため初心者でも安心して使えるにょ。
したがって、「固定小数点は初心者向き」と書いたもののあくまで1つに型を絞るならば浮動
小数点よりも固定小数点の方が初心者に向いているというだけのことにょ。
|
|
|
掲示板管理者へ連絡
無料レンタル掲示板