Skip to content

Instantly share code, notes, and snippets.

@komasaru
Last active Apr 19, 2018
Embed
What would you like to do?
Ruby script to compute big-digit floats.
#! /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