Last active
April 19, 2018 05:11
-
-
Save komasaru/5442441 to your computer and use it in GitHub Desktop.
Ruby script to compute big-digit floats.
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/ruby | |
#********************************************* | |
# 浮動小数点数加減算 | |
#********************************************* | |
# | |
class AddBigFloat | |
D_MAX = 1001 # 有効桁数(小数点以下+べき指数) | |
def initialize | |
# 使用する被加減数・加減数を設定(テストなので適当な乱数を使用) | |
# ( A + B, A - C で A > C となることが条件だが | |
# 稀に条件に合致しない値が生成されるかもしれない ) | |
@a, @b, @c = Array.new, Array.new, Array.new | |
@a << rand(D_MAX - 1) + 1 # Aのべき指数部 | |
@b << rand(D_MAX - 1) + 1 # Bのべき指数部 | |
@c << @a[0] - rand(@a[0]) - 1 # Cのべき指数部 | |
(D_MAX - 1).times do |_| | |
@a << rand(10) # Aの小数部 | |
@b << rand(10) # Bの小数部 | |
@c << rand(10) # Cの小数部 | |
end | |
end | |
# 計算 | |
def calc | |
# 計算する被加減数・加数・減数を出力 | |
puts "A ="; fdisp(@a) | |
puts "B ="; fdisp(@b) | |
puts "C ="; fdisp(@c) | |
# 計算(Z = A + B) ・結果出力 | |
puts "A + B =" | |
fdisp(fadd(@a, @b, 1)) | |
# 計算(Z = A - C) ・結果出力 | |
puts "A - C =" | |
fdisp(fadd(@a, @c, -1)) | |
rescue => e | |
raise | |
end | |
private | |
# ======================================================== | |
# 加減算 | |
# ( 固定長整数同士の加減算, 正規化含む ) | |
# | |
# [引数] a : 被加減数配列 | |
# b : 加減数配列 | |
# id : 1: 加算(A + B(, -1: 減算(A - B) | |
# [返り値] z : 計算結果配列 | |
# ======================================================== | |
def add(a, b, id) | |
z = Array.new # 結果格納配列 | |
begin | |
# 計算 | |
if id >= 0 # 加算 | |
a.size.times { |i| z << a[i] + b[i] } | |
else # 減算 | |
a.size.times { |i| z << a[i] - b[i] } | |
end | |
# 正規化 | |
z = norm(z) | |
return z | |
rescue => e | |
raise | |
end | |
end | |
# ======================================================== | |
# 加減算 | |
# ( 浮動小数点数同士の加減算, 正規化含む ) | |
# | |
# [引数] a : 被加減数配列 | |
# b : 加減数配列 | |
# id : 1: 加算(A + B), -1: 減算(A - B) | |
# [返り値] z : 出力データ配列(Z = A + B or Z = A - B) | |
# ======================================================== | |
def fadd(a, b, id) | |
z = Array.new # 結果格納配列 | |
begin | |
# 計算 | |
if a[0] >= b[0] # A のべき指数 >= B のべき指数 | |
k = a[0] - b[0] | |
z = a[0...(k + 1)] | |
z += add(a[(k + 1)..-1], b[1..(a.size - 1 + k)], id) | |
else # A のべき指数 < B のべき指数 | |
z << b[0] | |
k = b[0] - a[0] | |
# 位の異なる部分 | |
if id >= 0 # 加算 | |
z += b[1..k] | |
else # 減算 | |
1.upto(k) { |i| z << -b[i] } | |
end | |
# 位の同じ部分の加減算 | |
z += add(a[1..(-k - 1)], b[(k + 1)..-1], id) | |
end | |
# 正規化 | |
z = [z[0]] + norm(z[1..-1]) # 固定長整数 | |
z = fnorm(z) # 浮動小数点数 | |
return z | |
rescue => e | |
raise | |
end | |
end | |
# ======================================================== | |
# 正規化 | |
# ( Fixed Normalize(A) by Base-Value ) | |
# | |
# [引数] a : 正規化前データ配列 | |
# [返り値] a : 正規化後データ配列 | |
# ======================================================== | |
def norm(a) | |
cr = 0 # 繰り上がり | |
begin | |
(a.size - 1).downto(1) do |i| | |
cr = a[i] / 10 | |
a[i] -= cr * 10 | |
a[i - 1] += cr | |
end | |
(a.size - 1).downto(1) do |i| | |
if a[i] < 0 | |
a[i - 1] -= 1 | |
a[i] += 10 | |
end | |
end | |
return a | |
rescue => e | |
raise | |
end | |
end | |
# ======================================================== | |
# 正規化 | |
# ( Floating Normalize(A) ) | |
# | |
# [引数] a : 正規化前データ配列 | |
# [返り値] a : 正規化後データ配列 | |
# ======================================================== | |
def fnorm(a) | |
k = 0 # 上位の 0 の個数 | |
begin | |
# 小数点以下第1位が桁あふれする場合、右に1桁シフト&べき指数加算 | |
if a[1] >= 10 | |
(a.size - 2).downto(2) { |i| a[i + 1] = a[i] } | |
a[2] = a[1] % 10 | |
a[1] = a[1] / 10 | |
a[0] += 1 | |
end | |
# 正規化前のべき指数を退避 | |
exp = a[0] | |
# 上位の 0 の個数をカウント | |
1.upto(a.size) do |i| | |
unless a[i] == 0 | |
k = i - 1 | |
break | |
end | |
end | |
# 上位の 0 の部分に以降の数字をシフト | |
1.upto(a.size - k) { |i| a[i] = a[i + k] } | |
# シフト後の下位に 0 をセット | |
(a.size - k).upto(a.size - 1) { |i| a[i] = 0 } | |
# べき指数セット | |
a[0] = exp - k | |
return a | |
rescue => e | |
raise | |
end | |
end | |
# ======================================================== | |
# 結果出力 (指数表示) | |
# | |
# [引数] s : 結果出力データ配列 | |
# [返り値] なし | |
# ======================================================== | |
def fdisp(s) | |
if s[1] < 0 | |
puts "ANS. < 0\nPlease retry!" | |
exit | |
end | |
print "0." | |
# 1行に50桁出力 | |
1.upto(s.size - 1) do |i| | |
print s[i] | |
print " " if i % 10 == 0 | |
print "\n " if i % 50 == 0 | |
end | |
# べき指数 | |
puts s[0] < 0 ? " * 10^(#{s[0]})" : " * 10^#{s[0]}" | |
puts | |
rescue => e | |
raise | |
end | |
end | |
if __FILE__ == $0 | |
begin | |
# 計算クラスインスタンス化 | |
obj = AddBigFloat.new | |
# 計算 | |
obj.calc | |
rescue => e | |
$stderr.puts "[#{e.class}] #{e.message}" | |
e.backtrace.each{ |tr| $stderr.puts "\t#{tr}" } | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment