Skip to content

Instantly share code, notes, and snippets.

@metanest
Created August 26, 2012 08:57
Show Gist options
  • Save metanest/3476393 to your computer and use it in GitHub Desktop.
Save metanest/3476393 to your computer and use it in GitHub Desktop.
#!/usr/local/bin/ruby19
class Acc
BITS = 36
MASK = ("1" * BITS).to_i 2
def to_s
s = @val.to_s 2
s = s.rjust BITS, "0"
s[1, 0] = "."
return "#{s} | #{@val} | #{@val / 2.0**(BITS-1)}"
end
# n をマジック数としてセット
def setmagic n
raise if n < 0
s = ""
m = ("1" + "0" * (n.to_s.size - 1)).to_i
while n != 0 do
if n >= m then
s += "1"
n -= m
else
s += "0"
end
m /= 2
end
@val = s.ljust(BITS, "0").to_i 2
end
# 末尾桁に足し込む
def add_tail n
@val += n
end
# 10 倍する
def mul10
@val *= 10
@val &= MASK
end
# 仕上げ
def fin
# (10^10 / 2^(BITS-1)) で割る
#
# PC-1 実機なら、
# 0.01001010100000010111110010000000000 で割る。
# == 0.2910383045673370361328125 で割る。
## あるいは逆数の 3.4359738368 を掛ける。
## 直接掛けることはできないので、
#
## 左に 1 ビットシフト
## 1/4 倍の 0.8589934592 を掛ける
## 左に 1 ビットシフト
#
## とすればよいか(いきなり左に 2 ビットシフトすると溢れる)
@val *= 2**(BITS-1)
@val /= 10000000000
end
end
unless /\A0\.[0-9]*\z/.match ARGV[0] then
puts "Example: ./pc1_decbin.rb 0.12345"
exit
end
input = ARGV[0][2 .. -1].dup
puts "Input: 0.#{input}"
puts
def decbin_pc1 str_in
str_in = str_in.dup
acc = Acc.new
# このマジックナンバーは 10 倍が 10 回起きるように仕組まれた数
acc.setmagic 1_193359375
p acc
until str_in.empty? do
acc.mul10
digit = str_in.slice! 0
puts "char: #{digit.inspect}"
acc.add_tail digit.to_i
p acc
end
while acc.to_s[0, 1] == "1" do
acc.mul10
p acc
end
acc.fin
p acc
end
# 現代風に。その 1
def modern1 str_in
str_in = str_in.dup
a = 0.0
b = 1
until str_in.empty? do
b *= 10
digit = str_in.slice! 0
a += digit.to_f / b.to_f
end
result = a
p result
end
# 現代風に。その 2
def modern2 str_in
str_in = str_in.dup
a = 0
b = 1
until str_in.empty? do
a *= 10 ; b *= 10
digit = str_in.slice! 0
a += digit.to_i
end
result = a.to_f / b.to_f
p result
end
modern1 input
modern2 input
decbin_pc1 input
=begin
マジックナンバーのつくりかた
初項 1、公比 0.5 の等比数列を先頭から順番にいくつか並べてみる。
1.0
0.5
0.25
0.125
0.0625
0.03125
0.015625
0.0078125
0.00390625
0.001953125
0.0009765625
0.00048828125
0.000244140625
0.0001220703125
2 進小数の「ある数」は、上記の数のそれぞれを、
選んだり(その桁が 1 の時)、選ばなかったり(その桁が 0 の時)して、
選んだ数の合計である。
最初にアキュムレータに置いた数は、繰り返し十倍されるわけだから、
十進で書いてあるこの表で考えるならば、どんどん左シフトされてゆく
わけである。
それが奇数であれば、1 の位の所にシフトされた時に、2 進では 1 である
わけだから、十進でどの桁も奇数であるような数が「マジックナンバー」と
なる。
ここでは 10 回 10 倍させたいわけだから、
1.0
0.5
0.25
0.125
0.0625
0.03125
0.015625
0.0078125
0.00390625
0.001953125 の中から選ぶことになる
まず、最下位桁のために 0.001953125 は選ばなければならない。
次の桁を 7 にするために 0.00390625 も選ぶ。
すると 0.0078125 は、選ぶと 5 + 2 + 1 == 8 になってしまうから選べない。
以上のような手続きを繰り返すと、以下のように数が選ばれる。
(必ず最下位桁は 5 であるから、そこより下の行での合計の偶奇に応じて
その行を選ぶか選ばないかすればよい)
○ 1.0
× 0.5
× 0.25
○ 0.125
○ 0.0625
× 0.03125
× 0.015625
× 0.0078125
○ 0.00390625
○ 0.001953125
--------------
1.193359375
以上のようにマジックナンバーが求まった。
2 進では 1.001100011 である。
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment