万能数値表現法 URR
━───────────────────────────────────
アセンブラ講座(番外編)
《万能数値表現法 URR》
鎌田 誠
────────────────────────────────────
IEEE 754 で規格化されている浮動小数点数の表現方法は符号と指数部と仮数
部に整然と分けられていてわかりやすく、実装も容易なのですが、指数部と仮数
部を区切る位置を固定してしまったために、大きな数を扱いたい技術者には指数
部の範囲が狭すぎ、精度を要求する技術者には仮数部のビット数が少なすぎると
いう問題点があります。
しかし、かつて日本人によって IEEE 754 よりも算術的に優れている浮動小数
点数の表現方法が考案されていたことを知る人はほとんどいないでしょう。その
数値表現法は考案された当時の技術では実装が困難だったために規格化されなか
ったようですが、非常に興味深い数値表現法なので紹介したいと思います。
● URR とは
その優れた数値表現法は、日立中央研究所の主任研究員だった浜田穂積氏によ
って提案されました。その算術的特徴から、万能数値表現法(URR: Universal
Representation of Real numbers)と呼ばれています。数学界で著名な一松信教
授も天才的と評したという逸話も残るこの数値表現法は、「実数をある規則で次
々に 2 分し続けることですべての実数を表現することができる」という着想に
基づいています。
●実数集合の区切り方
まず、実数全体を正と負に分けます。ここでは慣例に従って正を 0、負を 1
とします。次に、正の数の場合は、1 以上と 1 未満に分け、値が大きい方を 1、
小さい方を 0 とします。続いて 1 以上の実数は 2 を堺に分け、2 以上を 1、2
未満を 0 とします。また、1 未満は 0.5 で分け、0.5 以上は 1、0.5 未満を 0
とします。負の数についても同様に、最初に -1 で分け、次に -0.5 と -2 で分
けます。
┌───────────────┐
│ │
│ 1 2.0 ─ │
│ │
│ 0 1.0 ────── │
│ │
│ 0 0.5 ─ │
│ │
│ 0.0 ─────────── │
│ │
│ 1 -0.5 ─ │
│ │
│ 1 -1.0 ────── │
│ │
│ 0 -2.0 ─ │
│ │
└───────────────┘
0 および ±∞ を含む区間は、普通ならば 2,4,8,16,32,…とするところを、
URR では 2,4,16,256,65536… のように急激に増加する数列で区切ってゆきます。
一見すると何だかよくわかりませんが、実はこれは二重指数による分割なのです。
つまり、2=2^(2^0),4=2^(2^1),16=2^(2^2),256=2^(2^3),65536=2^(2^4),… とい
う具合いです。
さらに、4〜16 などの区間は両端の値の幾何平均(2 の指数の算術平均)で分
けます。例えば、4=2^(2^1)〜16=2^(2^2) は √(4×16)=8=2^((2^1+2^2)/2) で
分け、16=2^(2^2)〜256=2^(2^3) は √(16×256)=64=2^((2^2+2^3)/2) で分ける
のです。
最後に、区間の両端の値の比が 1:2 になったら、その区間の残りはすべて算
術平均で区切ります。例えば、1〜2 は 1.5 で区切り、1.5〜2 は 1.75 で区切
ります。
このようにして実数の集合を分割していくと、0 や ±∞ の近くでは急激に大
きな数や小さな数を表現できる一方で、使用頻度の高い 1 の近辺は密に区切ら
れるという、まさに理想的な数値表現法ができあがるのです。
● URR と 10 進数表記の対応表
URR と 10 進数表記の対応表を、とりあえず 6 ビットの URR について書いて
みましょう。
┌───┬───────┐ ┌───┬───────┐
│ URR │10進数表記 │ │ URR │10進数表記 │
├───┼───────┤ ├───┼───────┤
│011111│256 │ │111111│-0.00390625 │
│011110│16 │ │111110│-0.0625 │
│011101│8 │ │111101│-0.125 │
│011100│4 │ │111100│-0.25 │
│011011│3.5 │ │111011│-0.3125 │
│011010│3 │ │111010│-0.375 │
│011001│2.5 │ │111001│-0.4375 │
│011000│2 │ │111000│-0.5 │
│010111│1.875 │ │110111│-0.5625 │
│010110│1.75 │ │110110│-0.625 │
│010101│1.625 │ │110101│-0.6875 │
│010100│1.5 │ │110100│-0.75 │
│010011│1.375 │ │110011│-0.8125 │
│010010│1.25 │ │110010│-0.875 │
│010001│1.125 │ │110001│-0.9375 │
│010000│1 │ │110000│-1 │
│001111│0.9375 │ │101111│-1.125 │
│001110│0.875 │ │101110│-1.25 │
│001101│0.8125 │ │101101│-1.375 │
│001100│0.75 │ │101100│-1.5 │
│001011│0.6875 │ │101011│-1.625 │
│001010│0.625 │ │101010│-1.75 │
│001001│0.5625 │ │101001│-1.875 │
│001000│0.5 │ │101000│-2 │
│000111│0.4375 │ │100111│-2.5 │
│000110│0.375 │ │100110│-3 │
│000101│0.3125 │ │100101│-3.5 │
│000100│0.25 │ │100100│-4 │
│000011│0.125 │ │100011│-8 │
│000010│0.0625 │ │100010│-16 │
│000001│0.00390625 │ │100001│-256 │
│000000│0 │ │100000│-∞ │
└───┴───────┘ └───┴───────┘
● URR への変換
例えば、37 という値を URR で表現することを考えてみましょう。
37≧2 なので、37 の URR は 011… で始まります。
37 は 2 進数で 100101 です。IEEE 754 式の書き方をすれば、
37 = (-1)^0×2^5×1.00101
~~~~~~~2進数表記
となります。ここで 2^5 の 5 も 2 進数にして二重指数で表現すると、
37 = (-1)^0×2^((-1)^0×2^2×1.01)×1.00101
~~~~ ~~~~~~~2進数表記
となります。2 以上の値を意味する 011 に続けて、指数部の指数部つまり 2 個
だけ 1 を続け、0 で止めます。つまり、011110… です。これで値が 16〜256
の範囲にあることがわかります。次に、指数部の仮数部の 1.01 から先頭の 1
を除いた 01 を続け、最後に仮数部の 1.00101 から先頭の 1 を除いた 00101
を続けます。これで 0111100100101… となります。データのサイズに合わせて
末尾に 0 を補えば、37 を表す URR になります。
● URR と IEEE 754 の比較
URR は ±1 の近辺で精度が高いと書きましたが、実際にどのくらい精度が高
いのか、IEEE 754 と比較してみましょう。
URR では、2 進数表記で 1.xxx… となる 1 以上 2 未満の数を 010xxx… で
表現できます。これは IEEE 754 で言うところの符号と指数部が合わせて 3 ビ
ットに収まっているということと同じです。IEEE 754 の 32 ビットの浮動小数
点数では符号と指数部だけで 9 ビットも消費しているので、URR のほうが有効
精度が 9-3=6 ビット(10 進数に換算して 1.8 桁)多く、そのぶん精度が高い
のです。64 ビットの浮動小数点数ならば 12-3=9 ビット(同じく 2.7 桁)分も
精度が高くなっています。
ところで、±1 の近辺という曖昧な書き方をしましたが、実際にはどのあたり
から IEEE 754 よりも URR の方が精度が低くなるのか考えてみましょう。32 ビ
ットで比較するならば、URR の符号と二重指数が合わせて 10 ビット以上になる
ところから IEEE 754 よりも精度が悪くなるということです。次の対応表で確認
しましょう。
┌───────────┬───────────────┐
│ URRの先頭 │ 表現される値の範囲 │
├───────────┼───────────────┤
│0 1 1 111110 00000 │4294967296〜8589934592 │
│0 1 1 11110 1111 │2147483648〜4294967296 │
│0 1 1 11110 1110 │1073741824〜2147483648 │
│0 1 1 11110 1101 │536870912〜1073741824 │
│0 1 1 11110 1100 │268435456〜536870912 │
│0 1 1 11110 1011 │134217728〜268435456 │
│0 1 1 11110 1010 │67108864〜134217728 │
│0 1 1 11110 1001 │33554432〜67108864 │
│0 1 1 11110 1000 │16777216〜33554432 │
│0 1 1 11110 0111 │8388608〜16777216 │
│0 1 1 11110 0110 │4194304〜8388608 │
│0 1 1 11110 0101 │2097152〜4194304 │
│0 1 1 11110 0100 │1048576〜2097152 │
│0 1 1 11110 0011 │524288〜1048576 │
│0 1 1 11110 0010 │262144〜524288 │
│0 1 1 11110 0001 │131072〜262144 │
│0 1 1 11110 0000 │65536〜131072 │
│0 1 1 1110 111 │32768〜65536 │
│0 1 1 1110 110 │16384〜32768 │
│0 1 1 1110 101 │8192〜16384 │
│0 1 1 1110 100 │4096〜8192 │
│0 1 1 1110 011 │2048〜4096 │
│0 1 1 1110 010 │1024〜2048 │
│0 1 1 1110 001 │512〜1024 │
│0 1 1 1110 000 │256〜512 │
│0 1 1 110 11 │128〜256 │
│0 1 1 110 10 │64〜128 │
│0 1 1 110 01 │32〜64 │
│0 1 1 110 00 │16〜32 │
│0 1 1 10 1 │8〜16 │
│0 1 1 10 0 │4〜8 │
│0 1 1 0 │2〜4 │
│0 1 0 │1〜2 │
│0 0 1 │0.5〜1 │
│0 0 0 1 │0.25〜0.5 │
│0 0 0 01 1 │0.125〜0.25 │
│0 0 0 01 0 │0.0625〜0.125 │
│0 0 0 001 11 │0.03125〜0.0625 │
│0 0 0 001 10 │0.015625〜0.03125 │
│0 0 0 001 01 │0.0078125〜0.015625 │
│0 0 0 001 00 │0.00390625〜0.0078125 │
│0 0 0 0001 111 │0.001953125〜0.00390625 │
│0 0 0 0001 110 │0.0009765625〜0.001953125 │
│0 0 0 0001 101 │0.00048828125〜0.0009765625 │
│0 0 0 0001 100 │0.000244140625〜0.00048828125 └┐
│0 0 0 0001 011 │0.0001220703125〜0.000244140625 └┐
│0 0 0 0001 010 │0.00006103515625〜0.0001220703125 └┐
│0 0 0 0001 001 │0.000030517578125〜0.00006103515625 └┐
│0 0 0 0001 000 │0.0000152587890625〜0.000030517578125 │
└───────────┴───────────────────┘
この表でわかるように、URR の符号と二重指数の表現が 9 ビット以内に収ま
っているのは絶対値が 0.00390625〜65536(2^-16〜2^16)の範囲にあるときで
す。この範囲を越えると、URR は IEEE 754 よりも精度が悪くなります。
同様に 64 ビットの場合を考えると、次のような、やや驚くべき事実が得られ
ます。
┌──────────────────────────────────┐
│ │
│ 64 ビットの実数の表現では、絶対値が 2^-16〜2^16 の範囲の数値に対 │
│ │
│ して URR は IEEE 754 よりも 2 ビット以上精度が高い。また、絶対値 │
│ │
│ が 2^-32〜2^32 の範囲の数値に対して URR は IEEE 754 と同等以上の │
│ │
│ 精度を持っている。 │
│ │
└──────────────────────────────────┘
このように、URR は ±1 の近辺で精度が高いだけでなく、実用上問題ないと
思われる範囲で十分な精度を持っているのです。
● URR の特徴
URR の特徴をまとめておきましょう。
(1) オーバーフローやアンダーフローが事実上発生しない
例えば 32 ビットの URR で表現できる数値の最大値は 10 進数に換算すると
1 億桁以上の数になります。IEEE 754 のように 0 や ±∞ を特別な数として例
外的な表現にする必要がありません。
(2) ±1 の近辺で精度が非常に高い
前述の通りです。
(3) データのサイズに関係なく一定のフォーマットである
URR では、精度を下げるときは末尾を切り捨て、精度を上げるときは末尾に 0
を補うだけで済みます。そのため、データのサイズの変更が容易です。
(4) 大小の比較が符号つき整数と同じ手順でできる
IEEE 754 では符号が独立しているため大小の比較を行うときに符号を分けて
考えなければなりませんが、URR では順序関係が通常の符号つき整数と一致して
いるので符号つき整数と同じ手順で大小の比較を行うことができます。
(5) 符号の反転が符号つき整数と同じ手順でできる
IEEE 754 のように 1 ビット反転するだけで済ませるというわけにはいきませ
んが、符号つき整数だと思って 0 から引くことで符号を反転することができま
す。
(6) デコードがやや面倒
IEEE 754 と比較すると、指数が二重になっている分だけデコードが面倒です。
(7) 例外的な表現を入れる余地がない
-∞ を切り崩すなどの工夫をしなければ、IEEE 754 で言うところの NaN(非
数)を表現することができません。
●最後に
URR は算術的に優れた驚異的な数値表現法であり、規格化されなかったのはと
ても残念です。
IEEE 754 の数値表現法は指数部分のビット数が制限されているために数値解
析などの分野では既に破綻しており、これらの分野では指数部を別の変数で管理
しなければならないことがあるなど、プログラマの負担が増加する原因になって
います。URR であれば指数部の範囲が事実上無制限なのでプログラムは簡単にな
り、しかも使用頻度が高い範囲では数値の精度も上がります(一般的に、変数を
増やして自前で浮動小数点演算を行うよりも、扱う数値の絶対値が ±1 に近付
くように演算方法を工夫するほうがずっと簡単です)。
最初に「考案された当時の技術では実装できなかったために規格化されなかっ
たようだ」と書きましたが、X68k の FLOAT2.X のように浮動小数点演算をソフ
トウェアで実装しようとした場合を考えると、今でも URR は複雑すぎると言わ
ざるを得ません。そう考えると、ひょっとしたら将来 URR が復活することがあ
るのではないかと、密かに期待しています。
━───────────────────────────────────
●あれこれ
本当は「FFT と畳み込みを使った多倍精度乗算」について書きたくて、その前
に多倍精度乗算について書く必要があって、その前に整数と浮動小数点数のフォ
ーマットについて書いておかなくちゃと思って、そうしたら URR のことを思い
出して……。
実は、私は学生時代に電気通信大学で教鞭をとられていた浜田穂積氏にお会い
したことがあったりします。URR の誕生秘話などをお聞きしたかったのですが、
氏は URR のことを過去の失敗作のようにおっしゃって多くを語っていただけな
かったのが残念でした。
(EOF)
Since December 19, 2000 ▲