Python - 多倍長浮動小数点数の加減算!

Updated:


Python3 で、多桁(多倍長)の浮動小数点同士で加減算する方法についてです。

0. 前提条件

  • LMDE 2 (Linux Mint Debian Edition 2; 64bit) での作業を想定。
  • Python 3.6.4 での作業を想定。
  • 当方は他のバージョンとの共存環境であり、 python3.6, pip3.6 で 3.6 系を使用するようにしている。(適宜、置き換えて考えること)

1. アルゴリズムについて

当ブログ過去記事を参照。

2. Python スクリプトの作成

  • 敢えてオブジェクト指向で作成している。
  • Shebang ストリング(1行目)では、フルパスでコマンド指定している。(当方の慣習
  • 数値計算ライブラリ NumPy を使用しない。(この程度の計算では、逆に2倍程度時間がかかってしまうため)
  • 必要であれば、スクリプト内の定数を変更する。
  • 今回、 A - C < 0 (A < C) になることは想定していない。

File: add_big_float.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#! /usr/local/bin/python3.6
"""
Comutation between two big-digit floats
"""
import random
import sys
import traceback


class AddBigFloat:
    D_MAX = 1001  # Number of significant digits

    def __init__(self):
        # Should be A > C
        self.a, self.b, self.c = [], [], []
        self.a.append(random.randrange(self.D_MAX - 1) + 1)
        self.b.append(random.randrange(self.D_MAX - 1) + 1)
        self.c.append(self.a[0] - random.randrange(self.a[0]) - 1)
        self.a += [random.randrange(10)for _ in range(self.D_MAX - 1)]
        self.b += [random.randrange(10)for _ in range(self.D_MAX - 1)]
        self.c += [random.randrange(10)for _ in range(self.D_MAX - 1)]

    def compute(self):
        """ Computation """
        try:
            print("A =")
            self.__fdisp(self.a)
            print("B =")
            self.__fdisp(self.b)
            print("C =")
            self.__fdisp(self.c)
            print("A + B =")
            self.__fdisp(self.__fadd(self.a, self.b, 1))
            print("A - C =")
            self.__fdisp(self.__fadd(self.a, self.c, -1))
        except Exception as e:
            raise

    def __add(self, a, b, flag):
        """ Addition or subtraction between two integers

        :param  list   a
        :param  list   b
        :param  int flag
        :return list   z
        """
        try:
            if flag >= 0:
                z = [a[i] + b[i] for i in range(len(a))]
            else:
                z = [a[i] - b[i] for i in range(len(a))]
            return self.__norm(z)
        except Exception as e:
            raise

    def __fadd(self, a, b, flag):
        """ Addition or subtraction between two floats

        :param  list   a
        :param  list   b
        :param  int flag
        :return list   z
        """
        z = []
        try:
            if a[0] >= b[0]:
                k = a[0] - b[0]
                z = a[:(k + 1)]
                z += self.__add(a[(k + 1):], b[1:(len(a) + k)], flag)
            else:
                z.append(b[0])
                k = b[0] - a[0]
                if flag >= 0:
                    z += b[1:(k + 1)]
                else:
                    z += [-b[i] for i in range(1, k + 1)]
                z += self.__add(a[1:(-k)], b[(k + 1):], flag)
            return self.__fnorm([z[0]] + self.__norm(z[1:]))
        except Exception as e:
            raise

    def __norm(self, a):
        """ Normalization of a integer

        :param  list a
        :return list a
        """
        cr = 0
        try:
            for i in range(len(a) - 1, 0, -1):
                cr        = int(a[i] / 10)
                a[i]     -= cr * 10
                a[i - 1] += cr
            for i in range(len(a) - 1, 0, -1):
                if a[i] < 0:
                    a[i - 1] -= 1
                    a[i]     += 10
            return a
        except Exception as e:
            raise

    def __fnorm(self, a):
        """ Normalization of a float

        :param  list a
        :return list a
        """
        k = 0
        try:
            if a[1] >= 10:
                for i in range(len(a) - 2, 1, -1):
                    a[i + 1] = a[i]
                a[2] = a[1] % 10
                a[1] = int(a[1] / 10)
                a[0] += 1
            exp = a[0]
            for i in range(1, len(a) + 1):
                if a[i] != 0:
                    k = i - 1
                    break
            for i in range(1, len(a) - k):
                a[i] = a[i + k]
            for i in range(len(a) - k, len(a)):
                a[i] = 0
            a[0] = exp - k
            return a
        except Exception as e:
            raise

    def __fdisp(self, s):
        """ Display

        :param list s
        """
        try:
            if s[1] < 0:
                print("ANS. < 0\nPlease retry!")
                sys.exit()
            print("0.", end="")
            for i in range(1, len(s)):
              print(s[i], end="")
              if i % 10 == 0:
                  print(" ", end="")
              if i % 50 == 0:
                  print("\n  ", end="")
            if s[0] < 0:
                print(" * 10^({})".format(s[0]))
            else:
                print(" * 10^{}".format(s[0]))
            print()
        except Exception as e:
            raise


if __name__ == '__main__':
    try:
        obj = AddBigFloat()
        obj.compute()
    except Exception as e:
        traceback.print_exc()
        sys.exit(1)

3. Python スクリプトの実行

まず、実行権限を付与。

$ chmod +x add_big_float.py

そして、実行。

$ ./add_big_float.py

以上





 

Sponsored Link

 

Comments