数の概念 | 例 | ||||
---|---|---|---|---|---|
複素数 | 実数 | 有理数 | 整数 | 自然数 | \( 1, \ 2, \ 3 \) |
ゼロ | \( 0 \) | ||||
負の整数 | \( -1, \ -2, \ -3 \) | ||||
有限小数 | 分数 | \( \displaystyle 0.5, \ \frac{3}{4} \) | |||
循環小数 | \( \displaystyle \frac{1}{3} \) | ||||
無理数 | 無限小数 | \( \displaystyle \sqrt{2}, \ \pi, \ \log2 \) | |||
虚数 | \( \displaystyle 3i, \ -5i \) |
サンプル・プログラム
固定小数
固定小数で 01101.011 は
\( \displaystyle 01101.011 = 2^3 + 2^2 + 2^0 + 2^{-2} + 2^{-3} = 13.375 \)となる。
5+3の固定小数では、整数部の範囲は +15~-16 で、小数部は 2-3 = 0.125 未満は扱うことができない(誤差になってしまう)。
データ幅を64ビットに増やしたとしても、固定小数の位置によって、その数量を計算するのにふさわしくない可能性がある。
このため、固定小数は、黎明期を除いてコンピュータで利用されることは無かった。
浮動小数
10進数の浮動小数は、たとえば -123.4 = -1.23×102 とあらわし、1.23が仮数、2が指数となる。
浮動小数はIEEE754という国際規格で定められており、符号は1ビット、仮数は23ビット、指数は8ビットの合計32ビットである。
コンピュータ内部では2進数となるから、表現可能な最小値は ±2-126 ≒ ±1.2×10-38、最大値は ±2127 ≒ ±3.4×1038 となる。32ビット整数で扱える最大値が 2.1×109 であることを考えると、それよりはるかに大きな数を扱うことができる。
また、浮動小数は3つのデータから成ることから、簡単なデータ構造であるといってもいい。

名数の計算では単位を揃える必要があることは、すでに述べたとおりだ。たとえば長さの単位を SI単位系であるメートルに統一すると、水素原子核の直径から宇宙の果てまでの距離まで、浮動小数の範囲で扱うことができる。
対象 | メートル |
---|---|
水素原子核の直径 | 1.75×10-15 |
最新CPUの配線の幅 | 1.4×10-8 |
インフルエンザウイルスの大きさ | 1.0×10-7 |
大腸菌の大きさ | 1.0~3.0×10-6 |
ヒトの毛髪の直径 | 1.0×10-4 |
日本人成人男子の平均身長 | 1.67×100 |
東京駅~新大阪駅 | 5.526×106 |
成田空港~ヒースロー空港 | 9.585×107 |
地球~月 | 3.844×1010 |
地球~太陽 | 1.496×1012 |
太陽~冥王星(平均距離) | 5.954×1013 |
最も近い恒星(プロキシマケンタウリ)までの距離 | 4.02×1016 |
天の川銀河中心部までの距離 | 2.65×1020 |
ソンブレロ星雲(M104)までの距離 | 2.65×1023 |
宇宙の果てまでの距離 | 1.31×1026 |

また、データ幅を64ビットに拡張した倍精度浮動小数も定義されている。
CやC#では、単精度を float、倍精度を double で型宣言する。
符号 | 仮数 | 指数 | 範囲 | |
---|---|---|---|---|
単精度 | 1ビット | 23ビット | 8ビット | ±1.2×10-38~±3.4×1038 |
倍精度 | 1ビット | 52ビット | 11ビット | ±2.2×10-308~±1.8×10308 |
数の精度
測定値の場合は有効数字として桁数を使うことが多いが、浮動小数の場合は指数のビット数が精度を左右する。
循環小数
このため、データ属性が浮動小数の場合、1 ÷ 3 × 3= 0.999‥‥≠1 ということが起こりうる。システム開発する際、処理系の制約を確認しておこう。

10進数では有限小数だが、2進数で循環小数になる場合もある。たとえば 0.4 がそうである。
\( \displaystyle 0.4 = 2^{-2} + 2^{-3} + 2^{-6} + 2^{-7}... \)
2: //演算誤差
3: $a = 6 - 5.6;
4: $b = 0.4;
5: echo $a - $b;
#演算誤差
a = 6 - 5.6
b = 0.4
print(a - b)
6: //演算誤差
7: int main() {
8: float a, b;
9: a = 6 - 4.6;
10: b = 0.4;
11: cout << setprecision(8) << setiosflags(ios::fixed) << a - b << endl;
12: return(0);
13: }
分数
#分数クラス
from fractions import Fraction
a = 6 - Fraction(56, 10)
b = Fraction(4, 10)
print(a - b)
PHPによる分数クラスの作成
メソッド | 機能 |
---|---|
pahooFrac($n, $d) | インスタンス生成。$nが分子、$mが分母。$n, $dは負数でも小数でもよい。 |
dec() | 分数を小数に変換する。 |
isFiniteDecimal() | 分数が有限小数かどうかが判定する。 |
printFrac($dec) | 分数表記を返す。$decがTRUEなら。有限小数の時は小数表記に変換する。 |
add($x, $reduce) | 分数クラス $x を加算する。$reduceがFALSEなら約分しない。 |
sub($x, $reduce) | 分数クラス $x を減算する。$reduceがFALSEなら約分しない。 |
mul($x, $reduce) | 分数クラス $x を乗算する。$reduceがFALSEなら約分しない。 |
div($x, $reduce) | 分数クラス $x を除算する。$reduceがFALSEなら約分しない。 |
17: class pahooFrac {
18: var $error, $errmsg; //エラーフラグ,エラーメッセージ
19: var $numerator; //分子
20: var $denominator; //分母
21:
22: /**
23: * コンストラクタ
24: * @param double $numerator 分子(小数可;初期値=0)
25: * @param double $denominator 分母(小数可;初期値=1)
26: * @return なし
27: */
28: function __construct($numerator=0, $denominator=1) {
29: $this->error = FALSE;
30: $this->errmsg = '';
31: $this->numerator = $numerator;
32: $this->denominator = $denominator;
33: $this->dec2frac();
34: }
35:
36: /**
37: * デストラクタ
38: * @param なし
39: * @return なし
40: */
41: function __destruct() {
42: }
43:
44: /**
45: * 符号は分子に寄せる
46: * @param なし
47: * @return なし
48: */
49: function __sign() {
50: if ($this->denominator < 0) {
51: $this->numerator *= (-1);
52: $this->denominator = abs($this->denominator);
53: }
54: }
55:
56: /**
57: * 小数を整数部と小数部に分解する
58: * @param double $a 小数
59: * @return array(整数部, 小数部)
60: */
61: function split_dec($a) {
62: $b = $a > 0 ? floor($a) : ceil($a);
63: $c = $a - $b;
64: return array($b, $c);
65: }
66:
67: /**
68: * 分子・分母を整数に正規化する
69: * @param なし
70: * @return なし
71: */
72: function dec2frac() {
73: if (is_float($this->numerator)) {
74: list($a, $b) = $this->split_dec($this->numerator);
75: $n = 0;
76: while (TRUE) {
77: list($c, $d) = $this->split_dec($b);
78: if ($d == 0) break;
79: $b *= 10;
80: $n++;
81: }
82: $this->numerator = $a * pow(10, $n) + $b;
83: $this->denominator *= pow(10, $n);
84: }
85: if (is_float($this->denominator)) {
86: list($a, $b) = $this->split_dec($this->denominator);
87: $n = 0;
88: while (TRUE) {
89: list($c, $d) = $this->split_dec($b);
90: if ($d == 0) break;
91: $b *= 10;
92: $n++;
93: }
94: $this->denominator = $a * pow(10, $n) + $b;
95: $this->numerator *= pow(10, $n);
96: }
97: $this->reduce();
98: }
99:
100: /**
101: * 分数を小数に変換する
102: * @param なし
103: * @return double 小数
104: */
105: function dec() {
106: if ($this->denominator == 0) {
107: $this->error = TRUE;
108: $this->errmsg = 'pahooFrac: illegal denominator';
109: $res = NULL;
110: } else {
111: $res = $this->numerator / $this->denominator;
112: }
113: return $res;
114: }
115:
116: /**
117: * 最大公約数を求める(ユークリッドの互除法)
118: * @param int $m, $n 整数
119: * @return int 最大公約数
120: */
121: function gcdEuclidean($m, $n) {
122: if ($n == 0) return $m;
123: return $this->gcdEuclidean($n, (int)$m % $n);
124: }
125:
126: /**
127: * 最小公倍数を求める(ユークリッドの互除法)
128: * @param int $m, $n 整数
129: * @return int 最小公倍数
130: */
131: function lcmEuclidean($m, $n) {
132: if ($n == 0) return $m;
133: return $m * $n / $this->gcdEuclidean($m, $n);
134: }
135:
136: /**
137: * 素数を求める(エラトステネスのふるい)
138: * @param int $max 最大値
139: * @param array $primes 素数を格納する配列
140: * @return array $primes
141: */
142: function EratosthenesSieve($max, &$primes) {
143: for ($i = 2; $i <= $max; $i++) {
144: $flag = TRUE;
145: for ($k = 2; $k < $i; $k++) {
146: if ($i % $k === 0) {
147: $flag = FALSE;
148: break;
149: }
150: }
151: if ($flag) $primes[$i] = 0;
152: }
153: return $primes;
154: }
155:
156: /**
157: * 素因数分解
158: * @param array $primes 素因数を格納する配列
159: * @param int $n 素因数分解する整数
160: * @return array $primes
161: */
162: function primeDecomposition($n, &$primes) {
163: $max = floor(sqrt($n));
164: $this->EratosthenesSieve($max, $primes);
165:
166: //2から平方根までの素因数を求める
167: for ($i = 2; $i <= $max; $i++) {
168: if (isset($primes[$i])) {
169: do {
170: $b = $n % $i;
171: if ($b == 0) {
172: $primes[$i]++;
173: $n = floor($n / $i);
174: }
175: } while ($b == 0);
176: }
177: }
178: //残った素数
179: if ($n > $max) $primes[$n] = 1;
180:
181: return $primes;
182: }
183:
184: /**
185: * 有限小数かどうか
186: * @param なし
187: * @return bool TRUE/FALSE
188: */
189: function isFiniteDecimal() {
190: $gcd = $this->gcdEuclidean($this->denominator, $this->numerator);
191: $max = $this->denominator / $gcd;
192:
193: //分母に2, 5以外の素因数が含まれているかどうか
194: $primes = array();
195: $this->primeDecomposition($max, $primes);
196: $res = TRUE;
197: foreach ($primes as $key=>$val) {
198: if ($key != 2 && $key != 5 && $val > 0) {
199: $res = FALSE;
200: break;
201: }
202: }
203: return $res;
204: }
205:
206: /**
207: * 約分する
208: * @param なし
209: * @return なし
210: */
211: function reduce() {
212: $m = $this->gcdEuclidean($this->numerator, $this->denominator);
213: $this->numerator /= $m;
214: $this->denominator /= $m;
215: $this->__sign();
216: }
217:
218: /**
219: * 分数表記を返す
220: * @param bool $dec = TRUE:有限小数なら小数表記
221: * @return string
222: */
223: function printFrac($dec=FALSE) {
224: return ($dec && $this->isFiniteDecimal()) ? sprintf("%g", $this->dec()) :
225: sprintf("%d/%d", $this->numerator, $this->denominator);
226: }
227:
228: /**
229: * 分数を加算する
230: * @param pahooFrac $x 加える分数
231: * @param bool TRUE:約分する/FALSE:約分しない
232: * @return なし
233: */
234: function add($x, $reduce=TRUE) {
235: $denominator = $this->lcmEuclidean($this->denominator, $x->denominator);
236: $this->numerator = $this->numerator * ($denominator / $this->denominator)
237: + $x->numerator * ($denominator / $x->denominator);
238: $this->denominator = $denominator;
239: $this->__sign();
240: if ($reduce) $this->reduce();
241: }
242:
243: /**
244: * 分数を減算する
245: * @param pahooFrac $x 減ずる分数
246: * @param bool TRUE:約分する/FALSE:約分しない
247: * @return なし
248: */
249: function sub($x, $reduce=TRUE) {
250: $denominator = $this->lcmEuclidean($this->denominator, $x->denominator);
251: $this->numerator = $this->numerator * ($denominator / $this->denominator)
252: - $x->numerator * ($denominator / $x->denominator);
253: $this->denominator = $denominator;
254: $this->__sign();
255: if ($reduce) $this->reduce();
256: }
257:
258: /**
259: * 分数を乗算する
260: * @param pahooFrac $x 乗ずる分数
261: * @param bool TRUE:約分する/FALSE:約分しない
262: * @return なし
263: */
264: function mul($x, $reduce=TRUE) {
265: $this->numerator *= $x->numerator;
266: $this->denominator *= $x->denominator;
267: $this->__sign();
268: if ($reduce) $this->reduce();
269: }
270:
271: /**
272: * 分数を除算する
273: * @param pahooFrac $x 除する分数
274: * @param bool TRUE:約分する/FALSE:約分しない
275: * @return なし
276: */
277: function div($x, $reduce=TRUE) {
278: $this->numerator *= $x->denominator;
279: $this->denominator *= $x->numerator;
280: $this->__sign();
281: if ($reduce) $this->reduce();
282: }
283:
284: // End of Class
11: //データ構造クラス
12: require_once('pahooDataStructure.php');
13:
14: // メイン・プログラム ======================================================
15: printf("\n");
16:
17: $b = new pahooFrac(1, 3); //分数
18:
19: $a = new pahooFrac(3, 5); //分数
20: printf("%s", $a->printFrac(FALSE));
21: $a->add($b); //加算
22: printf(" + %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
23:
24: $a = new pahooFrac(0.6, 1); //分数;分子が小数でもOK
25: printf("%s", $a->printFrac(FALSE));
26: $a->sub($b); //減算
27: printf(" - %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
28:
29: $a = new pahooFrac(1.5, 2.5); //分数;分母が小数でもOK
30: printf("%s", $a->printFrac(FALSE));
31: $a->mul($b); //乗算
32: printf(" × %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
33:
34: $a = new pahooFrac(3, 5); //分数
35: printf("%s", $a->printFrac(FALSE));
36: $a->div($b); //除算
37: printf(" ÷ %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
38:
39: /*

実行結果は左図の通り。3分の1に関わる計算も循環小数とならない。
無理数
ここでも誤差が問題になる。
2: //無理数
3: $a = sqrt(3);
4: $b = $a * $a;
5: echo $b;
#無理数
import math
a = math.sqrt(3)
b = a * a
print(b)
コラム:虚数

なぜXY平面で描かないかというと、波形処理では、波形上にある点を微分で求めることが多い。このとき、XY平面では2つの式を処理するか、三角関数を使わなければならないが、複素平面だと \( a + bi \) という1つの式で表すことができ、計算量が減る。微分方程式は計算量が多いので、元の方程式の計算量が減ることで、プログラムを簡素化できたり、ハードウェア回路を小型化、高速化することができる。さらに、複素フーリエ級数からフーリエ変換を導出できる。

フーリエ変換は、少ない計算量で様々な波形処理ができることから、ノイズフィルタ、イコライザはもちろん、AIに学習させるデータを前処理する工程や、統計・検定でも活躍する。
フーリエ変換は理系のためのツールではない。景気変動やデリバティブにも適用されている。文系の方、とくに経済・金融関係の仕事をされている方は、虚数の存在を思い出してほしい。
詳しくは『今日から使えるフーリエ変換 普及版』(三谷政昭,2019年4月)をご覧いただきたい。

だが、詳しくは専門書をご覧いただきたいのだが、このグラフに虚軸を加えると、グラフは曲面となり、X軸を含む平面と交わる部分が出てくる。