Created
August 26, 2012 08:57
-
-
Save metanest/3476393 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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