Radix Pointに関するウィキペディアの記事を考えると、10.1 に相当する 2 進数または 17.17 に相当する 16 進数をどのように計算しますか? 前者の場合、10 分の 1 に相当する 2 進数は何ですか? 後者の場合、17/100 の 16 進数表現は?
私は、これら 2 つの例の解決策よりもアルゴリズムを探しています。
Radix Pointに関するウィキペディアの記事を考えると、10.1 に相当する 2 進数または 17.17 に相当する 16 進数をどのように計算しますか? 前者の場合、10 分の 1 に相当する 2 進数は何ですか? 後者の場合、17/100 の 16 進数表現は?
私は、これら 2 つの例の解決策よりもアルゴリズムを探しています。
10進数の10.1を2進数に変換するには、整数部分と小数部分を分離し、それぞれを別々に変換します。
整数部分を変換するには、2による整数除算を繰り返してから、余りを逆の順序で書き込みます。
10/2=5剰余0
5/2=2剰余1
2/2=1剰余0
1/2=0剰余1
回答:1010
小数部分を変換するには、2を繰り返し乗算し、各ステップで整数部分を減算します。整数部分は、生成順に、2進数を表します。
0.1 * 2 = 0.2
0.2 * 2 = 0.4
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4
0.4 * 2 = 0.8
...(サイクルは永遠に繰り返されます)
したがって、10進数の0.1は2進数の0.000110011001100です。
(より詳細な説明については、私の記事http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/のルーチンdec2bin_i()およびdec2bin_f()を参照してください。)
16進数の場合は、2ではなく16の除数/乗数を使用することを除いて、同じ手順を使用します。9より大きい剰余と整数部分は、16進数に直接変換する必要があります。10はAになり、11はBになり、...、15はFになります。 。
基数b 1 の終端数(有限桁数で表すことができる数)n 1 は、異なる基数b2の非終端数になる可能性がある。逆に、1つの基数b1の非終端数は、基数b 2の終端数であることが判明する場合がある。
2 進数に変換した場合の 0.1 10は、16 進数に変換した場合の0.17 10と同様に、終了しない数値です。しかし、基数 3 の終了数 0.1 3は、基数 10 に変換すると、非終了の繰り返し数 0.(3) 10になります (数 3 が繰り返されることを意味します)。同様に、0.1 10を 2 進数に変換し、0.17 10を 16 進数に変換すると、0.0(0011) 2と 0.2(B851E) 16の繰り返される非終了の数値になります。
このため、このような数値をある基数から別の基数に変換する場合、完全に正確な表現ではなく、数値を概算する必要があることに気付く場合があります。
アルゴリズムは非常に単純ですが、実際にはルックアップ テーブルとログの両方で多くの微調整を行って高速化できます。ただし、基本的なアルゴリズムについては、次のようなものを試すことができます。
shift=0;
while v>=base, v=v/base, shift=shift+1;
Next digit:
if v<1.0 && shift==0, output the decimal point
else
D=floor(v)
output D
v=v-D
v=v*base
shift = shift-1
if (v==0) exit;
goto Next Digit
10 進数をより長く繰り返すために、N 桁の後に印刷を停止するテストをそこに入れることもできます。
10 分の 1 の「2 進法」は 2 分の 1 です。つまり、1/10^1 ではなく、1/2^1 です。
各桁は 2 の累乗を表します。基数の後ろの数字は同じですが、1 の 2 乗を表しているだけです。
8 4 2 1 . 1/2 1/4 1/8 1/16
したがって、10.1 の場合、10 の部分を作成するには、明らかに「8」と「2」が必要です。1/2 (0.5) は多すぎます。1/4 (0.25) は多すぎます。1/8 (0.125) は多すぎます。1/16 (0.0625) が必要なので、残りは 0.0375 です。1/32 は 0.03125 なので、それも取ることができます。これまでのところ、次のことがわかっています。
8 4 2 1 . 1/2 1/4 1/8 1/16 1/32
1 0 1 0 0 0 0 1 1
0.00625 の誤差があります。1/64 (0.015625) と 1/128 (0.0078125) はどちらも大きすぎます。1/256 (0.00390625) が機能します。
8 4 2 1 . 1/2 1/4 1/8 1/16 1/32 1/64 1/128 1/256
1 0 1 0 0 0 0 1 1 0 0 1
0.00234375 の誤差があります。
.1 は 2 進数で正確に表現できません (1/3 を 10 進数で正確に表現できないのと同様)。基数を置く場所に応じて、最終的には停止し、おそらく丸め、エラーを受け入れる必要があります。
私の GMP ライブラリーに照らしてこれをいじる前に、Rick Regan の PHP コードを 2 から 36 までの任意の基数に対して汎用的にしようとしたところです。
Function dec2base_f(ByVal ddecimal As Double, ByVal nBase As Long, ByVal dscale As Long) As String
Const BASES = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 'up to base 36
Dim digitCount As Long
Dim wholeNumber As Double
Dim digit As String * 1
digitCount = 0
dscale = max(dscale, Len(CStr(ddecimal)) - Len("0."))
Dim baseary_f As String
baseary_f = "0."
Do While ddecimal > 0 And digitCount < dscale
ddecimal = ddecimal * nBase
digit = Mid$(BASES, Fix(ddecimal) + 1)
baseary_f = baseary_f & digit '"1"
ddecimal = ddecimal - Fix(ddecimal)
digitCount = digitCount + 1
Loop
dec2base_f = baseary_f
End Function
Function base2dec_f(ByVal baseary_f As String, nBase As Double) As Double
Const BASES As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim decimal_f As Double
Dim i As Long
Dim c As Long
For i = Len(baseary_f) To Len("0.") + 1 Step -1
c = InStr(BASES, Mid$(baseary_f, i, 1)) - 1
decimal_f = decimal_f + c
decimal_f = decimal_f / nBase
Next
base2dec_f = decimal_f
End Function
Debug.Print base2dec_f(dec2base_f(0.09, 2, 200), 2) --> 0.09
Debug.Print base2dec_f(dec2base_f(0.09, 8, 200), 8) --> 0.09
Debug.Print base2dec_f(dec2base_f(0.09, 16, 200), 16) --> 0.09