数の概念 | 例 | ||||
---|---|---|---|---|---|
複素数 | 実数 | 有理数 | 整数 | 自然数 | ![]() |
ゼロ | ![]() | ||||
負の整数 | ![]() | ||||
有限小数 | 分数 | ![]() | |||
循環小数 | ![]() | ||||
無理数 | 無限小数 | ![]() | |||
虚数 | ![]() |
サンプル・プログラム
固定小数
固定小数で 01101.011 は
01101.011 = 23 + 22 + 20 + 2-2 + 2-3 = 13.375
となる。
5+3 の固定小数では、整数部の範囲は +15~-16 で、小数部は 2-3 = 0.125 未満は扱うことができない(誤差になってしまう)。
データ幅を 64 ビットに増やしたとしても、固定小数の位置によって、その数量を計算するのにふさわしくない可能性がある。
このため、固定少数は、黎明期を除いてコンピュータで利用されることは無かった。
浮動小数
たとえば -123.4 = -1.23×102 とあらわし、1.23 が仮数、2 が指数となる。
浮動小数は IEEE754 という国際規格で定められており、符号は 1 ビット、仮数は 23 ビット、指数は 8 ビットの合計32 ビットである。
表現可能な最小値は ±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 がそうである。
0.4 = 2-2 + 2-3 + 2-6 + 2-7‥‥
0001: <?php
0002: //演算誤差
0003: $a = 6 - 5.6;
0004: $b = 0.4;
0005: echo $a - $b;
0006: ?>
0001: #演算誤差
0002: a = 6 - 5.6
0003: b = 0.4
0004: print(a - b)
0001: #include <iostream>
0002: #include <iomanip>
0003: #include <limits>
0004: using namespace std;
0005:
0006: //演算誤差
0007: int main() {
0008: float a, b;
0009: a = 6 - 4.6;
0010: b = 0.4;
0011: cout << setprecision(8) << setiosflags(ios::fixed) << a - b << endl;
0012: return(0);
0013: }
分数
0001: #分数クラス
0002: from fractions import Fraction
0003: a = 6 - Fraction(56, 10)
0004: b = Fraction(4, 10)
0005: 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なら約分しない。 |
0013: // pahooFracクラス =========================================================
0014: class pahooFrac {
0015: var $error, $errmsg; //エラーフラグ,エラーメッセージ
0016: var $numerator; //分子
0017: var $denominator; //分母
0018:
0019: /**
0020: * コンストラクタ
0021: * @param double $numerator 分子(小数可;初期値=0)
0022: * @param double $denominator 分母(小数可;初期値=1)
0023: * @return なし
0024: */
0025: function __construct($numerator=0, $denominator=1) {
0026: $this->error = FALSE;
0027: $this->errmsg = '';
0028: $this->numerator = $numerator;
0029: $this->denominator = $denominator;
0030: $this->dec2frac();
0031: }
0032:
0033: /**
0034: * デストラクタ
0035: * @param なし
0036: * @return なし
0037: */
0038: function __destruct() {
0039: }
0040:
0041: /**
0042: * 符号は分子に寄せる
0043: * @param なし
0044: * @return なし
0045: */
0046: function __sign() {
0047: if ($this->denominator < 0) {
0048: $this->numerator *= (-1);
0049: $this->denominator = abs($this->denominator);
0050: }
0051: }
0052:
0053: /**
0054: * 小数を整数部と小数部に分解する
0055: * @param double $a 小数
0056: * @return array(整数部, 小数部)
0057: */
0058: function split_dec($a) {
0059: $b = $a > 0 ? floor($a) : ceil($a);
0060: $c = $a - $b;
0061: return array($b, $c);
0062: }
0063:
0064: /**
0065: * 分子・分母を整数に正規化する
0066: * @param なし
0067: * @return なし
0068: */
0069: function dec2frac() {
0070: if (is_float($this->numerator)) {
0071: list($a, $b) = $this->split_dec($this->numerator);
0072: $n = 0;
0073: while (TRUE) {
0074: list($c, $d) = $this->split_dec($b);
0075: if ($d == 0) break;
0076: $b *= 10;
0077: $n++;
0078: }
0079: $this->numerator = $a * pow(10, $n) + $b;
0080: $this->denominator *= pow(10, $n);
0081: }
0082: if (is_float($this->denominator)) {
0083: list($a, $b) = $this->split_dec($this->denominator);
0084: $n = 0;
0085: while (TRUE) {
0086: list($c, $d) = $this->split_dec($b);
0087: if ($d == 0) break;
0088: $b *= 10;
0089: $n++;
0090: }
0091: $this->denominator = $a * pow(10, $n) + $b;
0092: $this->numerator *= pow(10, $n);
0093: }
0094: $this->reduce();
0095: }
0096:
0097: /**
0098: * 分数を小数に変換する
0099: * @param なし
0100: * @return double 小数
0101: */
0102: function dec() {
0103: if ($this->denominator == 0) {
0104: $this->error = TRUE;
0105: $this->errmsg = 'pahooFrac: illegal denominator';
0106: $res = NULL;
0107: } else {
0108: $res = $this->numerator / $this->denominator;
0109: }
0110: return $res;
0111: }
0112:
0113: /**
0114: * 最大公約数を求める(ユークリッドの互除法)
0115: * @param int $m, $n 整数
0116: * @return int 最大公約数
0117: */
0118: function gcdEuclidean($m, $n) {
0119: if ($n == 0) return $m;
0120: return $this->gcdEuclidean($n, (int)$m % $n);
0121: }
0122:
0123: /**
0124: * 最小公倍数を求める(ユークリッドの互除法)
0125: * @param int $m, $n 整数
0126: * @return int 最小公倍数
0127: */
0128: function lcmEuclidean($m, $n) {
0129: if ($n == 0) return $m;
0130: return $m * $n / $this->gcdEuclidean($m, $n);
0131: }
0132:
0133: /**
0134: * 素数を求める(エラトステネスのふるい)
0135: * @param int $max 最大値
0136: * @param array $primes 素数を格納する配列
0137: * @return array $primes
0138: */
0139: function EratosthenesSieve($max, &$primes) {
0140: for ($i = 2; $i <= $max; $i++) {
0141: $flag = TRUE;
0142: for ($k = 2; $k < $i; $k++) {
0143: if ($i % $k === 0) {
0144: $flag = FALSE;
0145: break;
0146: }
0147: }
0148: if ($flag) $primes[$i] = 0;
0149: }
0150: return $primes;
0151: }
0152:
0153: /**
0154: * 素因数分解
0155: * @param array $primes 素因数を格納する配列
0156: * @param int $n 素因数分解する整数
0157: * @return array $primes
0158: */
0159: function primeDecomposition($n, &$primes) {
0160: $max = floor(sqrt($n));
0161: $this->EratosthenesSieve($max, $primes);
0162:
0163: //2から平方根までの素因数を求める
0164: for ($i = 2; $i <= $max; $i++) {
0165: if (isset($primes[$i])) {
0166: do {
0167: $b = $n % $i;
0168: if ($b == 0) {
0169: $primes[$i]++;
0170: $n = floor($n / $i);
0171: }
0172: } while ($b == 0);
0173: }
0174: }
0175: //残った素数
0176: if ($n > $max) $primes[$n] = 1;
0177:
0178: return $primes;
0179: }
0180:
0181: /**
0182: * 有限小数かどうか
0183: * @param なし
0184: * @return bool TRUE/FALSE
0185: */
0186: function isFiniteDecimal() {
0187: $gcd = $this->gcdEuclidean($this->denominator, $this->numerator);
0188: $max = $this->denominator / $gcd;
0189:
0190: //分母に2, 5以外の素因数が含まれているかどうか
0191: $primes = array();
0192: $this->primeDecomposition($max, $primes);
0193: $res = TRUE;
0194: foreach ($primes as $key=>$val) {
0195: if ($key != 2 && $key != 5 && $val > 0) {
0196: $res = FALSE;
0197: break;
0198: }
0199: }
0200: return $res;
0201: }
0202:
0203: /**
0204: * 約分する
0205: * @param なし
0206: * @return なし
0207: */
0208: function reduce() {
0209: $m = $this->gcdEuclidean($this->numerator, $this->denominator);
0210: $this->numerator /= $m;
0211: $this->denominator /= $m;
0212: $this->__sign();
0213: }
0214:
0215: /**
0216: * 分数表記を返す
0217: * @param bool $dec = TRUE:有限小数なら小数表記
0218: * @return string
0219: */
0220: function printFrac($dec=FALSE) {
0221: return ($dec && $this->isFiniteDecimal()) ? sprintf("%g", $this->dec()) :
0222: sprintf("%d/%d", $this->numerator, $this->denominator);
0223: }
0224:
0225: /**
0226: * 分数を加算する
0227: * @param pahooFrac $x 加える分数
0228: * @param bool TRUE:約分する/FALSE:約分しない
0229: * @return なし
0230: */
0231: function add($x, $reduce=TRUE) {
0232: $denominator = $this->lcmEuclidean($this->denominator, $x->denominator);
0233: $this->numerator = $this->numerator * ($denominator / $this->denominator)
0234: + $x->numerator * ($denominator / $x->denominator);
0235: $this->denominator = $denominator;
0236: $this->__sign();
0237: if ($reduce) $this->reduce();
0238: }
0239:
0240: /**
0241: * 分数を減算する
0242: * @param pahooFrac $x 減ずる分数
0243: * @param bool TRUE:約分する/FALSE:約分しない
0244: * @return なし
0245: */
0246: function sub($x, $reduce=TRUE) {
0247: $denominator = $this->lcmEuclidean($this->denominator, $x->denominator);
0248: $this->numerator = $this->numerator * ($denominator / $this->denominator)
0249: - $x->numerator * ($denominator / $x->denominator);
0250: $this->denominator = $denominator;
0251: $this->__sign();
0252: if ($reduce) $this->reduce();
0253: }
0254:
0255: /**
0256: * 分数を乗算する
0257: * @param pahooFrac $x 乗ずる分数
0258: * @param bool TRUE:約分する/FALSE:約分しない
0259: * @return なし
0260: */
0261: function mul($x, $reduce=TRUE) {
0262: $this->numerator *= $x->numerator;
0263: $this->denominator *= $x->denominator;
0264: $this->__sign();
0265: if ($reduce) $this->reduce();
0266: }
0267:
0268: /**
0269: * 分数を除算する
0270: * @param pahooFrac $x 除する分数
0271: * @param bool TRUE:約分する/FALSE:約分しない
0272: * @return なし
0273: */
0274: function div($x, $reduce=TRUE) {
0275: $this->numerator *= $x->denominator;
0276: $this->denominator *= $x->numerator;
0277: $this->__sign();
0278: if ($reduce) $this->reduce();
0279: }
0280:
0281: // End of Class
0282: }
0011: //データ構造クラス
0012: require_once('pahooDataStructure.php');
0013:
0014: // メイン・プログラム ======================================================
0015: printf("\n");
0016:
0017: $b = new pahooFrac(1, 3); //分数
0018:
0019: $a = new pahooFrac(3, 5); //分数
0020: printf("%s", $a->printFrac(FALSE));
0021: $a->add($b); //加算
0022: printf(" + %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
0023:
0024: $a = new pahooFrac(0.6, 1); //分数;分子が小数でもOK
0025: printf("%s", $a->printFrac(FALSE));
0026: $a->sub($b); //減算
0027: printf(" - %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
0028:
0029: $a = new pahooFrac(1.5, 2.5); //分数;分母が小数でもOK
0030: printf("%s", $a->printFrac(FALSE));
0031: $a->mul($b); //乗算
0032: printf(" × %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));
0033:
0034: $a = new pahooFrac(3, 5); //分数
0035: printf("%s", $a->printFrac(FALSE));
0036: $a->div($b); //除算
0037: printf(" ÷ %s = %s\n", $b->printFrac(FALSE), $a->printFrac(TRUE));

実行結果は左図の通り。3 分の 1 に関わる計算も循環小数とならない。
無理数
ここでも誤差が問題になる。
0001: <?php
0002: //無理数
0003: $a = sqrt(3);
0004: $b = $a * $a;
0005: echo $b;
0006: ?>
0001: #無理数
0002: import math
0003: a = math.sqrt(3)
0004: b = a * a
0005: print(b)
0001: #include <iostream>
0002: #include <iomanip>
0003: #include <limits>
0004: #include <cmath>
0005: using namespace std;
0006:
0007: //無理数
0008: int main() {
0009: float a, b;
0010: a = sqrt(3);
0011: b = a * a;
0012: cout << setprecision(8) << setiosflags(ios::fixed) << b << endl;
0013: return(0);
0014: }