Cには標準ライブラリの中に数学関数が用意されており、ヘッダ <math.h> をインクルードすることで使用可能となる。ヘッダ <math.h>内には下記の関数などが具体的に宣言されている。
| sin(x) | 三角関数 \( sin(x) \)を計算する。 |
| cos(x) | 三角関数 \( cos(x) \)を計算する。 |
| tan(x) | 三角関数 \( tan(x) \)を計算する。 |
| asin(x) | 逆三角関数 \( sin^{-1}(x) \) を計算する。\( (-1 \le x \le 1) \) |
| acos(x) | 逆三角関数 \( cos^{-1}(x) \)を計算する。\( (-1 \le x \le 1) \) |
| atan(x) | 逆三角関数 \( tan^{-1}(x) \)を計算する。\( (-\infty \le x \le \infty) \) |
| atan2(y,x) | 逆三角関数 \( tan^{-1}(y/x) \)を計算する。 |
| sinh(x) | 双曲線関数 \( sinh(x) \)を計算する。 |
| cosh(x) | 双曲線関数 \( cosh(x) \)を計算する。 |
| tanh(x) | 双曲線関数 \( tanh(x) \)を計算する。 |
| exp(x) | 指数関数 \( e^{x} \) を計算する。 |
| log(x) | 自然対数 \( \ln x \) を計算する。\( (x \gt 0) \) |
| log10(x) | 10 を底とする対数 \( \log_{10} x \) を計算する。\( (x \gt 0) \) |
| pow(x,y) | べき乗 \( x^{y} \) を計算する。 |
| sqrt(x) | 実数\( x \)の平方根を求める。\( (x \gt 0) \) |
| ceil(x) | 実数\( x \)の小数部を切り上げた整数を返す。 |
| floor(x) | 実数\( x \)の小数部を切り捨てた整数を返す。 |
| fabs(x) | 絶対値 \( |x| \) を求める。 |
| ldexp(x,n) | \(x・2^{n} \) を計算する。 |
| frexp(x,&exp) | 実数\( x \)を仮数部と指数部(2のべき乗)\( exp \)に分解する。 |
| modf(x,&ip) | 実数\( x \)を整数部\( ip \)と小数部に分ける。 |
| fmod(x,y) | 実数の剰余( x % y に相当)を求める。 |
以下のコードの(1)と(2)に値を入力して数学関数を使った計算を実行しよう。
#include <stdio.h>
#include <math.h>
int main(void)
{
double x=__(1)__ ,y=__(2)__ ;
printf("x = %f, y = %f\n", x, y);
printf("x^y = %f\n", pow(x,y));
printf("atan2(y/x) = %f\n", atan2(y, x));
printf("sqrt(x*x+y*y) = %f\n", sqrt(pow(x,2) + pow(y,2)));
return 0;
}
[実行結果(例)] ----------------------- x = 3.000000, y = 4.000000 x^y = 81.000000 atan2(y/x) = 0.927295 sqrt(x*x+y*y) = 5.000000 ----------------------- x = 5.000000, y = 12.000000 x^y = 244140625.000000 atan2(y/x) = 1.176005 sqrt(x*x+y*y) = 13.000000
ヘッダ<stdlib.h>には、いくつかの有用な数値変換関数や整数乱数発生関数が宣言されている。
| atof(s) | 文字列 s を double型浮動小数点数に変換する。 |
| atoi(s) | 文字列 s を int型整数に変換する。 |
| atol(s) | 文字列 s を long型整数に変換する。 |
| rand() | 0~RAND_MAXの範囲の整数乱数を発生する。 |
| srand(seed) | rand() の種として seed をセットする。 |
| abs(n) | int型整数 \(n\) の絶対値 \(|n|\) を返す。 |
| labs(n) | long型整数 \(n\) の絶対値 \(|n|\) を返す。 |
ヘッダ <float.h>には浮動小数点数に関する様々な定数が定義されている。
| 単精度 | |
|---|---|
| _FLT_RADIX | 指数表現における基数 |
| _FLT_ROUNDS | 丸めのモード |
| FLT_DIG | 精度の10進けた数 |
| FLT_EPSILON | 計算機イプシロン |
| FLT_MANT_DIG | 浮動小数点数の有効数字部(_FLT_RADIXを基数とする)のけた数 |
| FLT_MAX | 表現可能な最大の有限浮動小数点数 |
| FLT_MAX_EXP | 表現可能な数の指数の最大値 |
| FLT_MIN | 最小の正規化された正の浮動小数点数 |
| FLT_MIN_EXP | 最小正規化数の指数の値 |
| 倍精度 | |
|---|---|
| _DBL_RADIX | 指数表現における基数 |
| _DBL_ROUNDS | 丸めのモード |
| DBL_DIG | 精度の10進けた数 |
| DBL_EPSILON | 計算機イプシロン |
| DBL_MANT_DIG | 浮動小数点数の有効数字部(_DBL_RADIXを基数とする)のけた数 |
| DBL_MAX | 表現可能な最大の有限浮動小数点数 |
| DBL_MAX_EXP | 表現可能な数の指数部の最大値 |
| DBL_MIN | 最小の正規化された正の浮動小数点数 |
| DBL_MIN_EXP | 最小正規化数の指数部の値 |
基本的に、数値計算には倍精度のdouble型を用いる。以下のコードを実行して、float型とdouble型の違いを確認しよう。
#include <stdio.h>
#include <float.h>
int main(void)
{
printf("float: %u bit, double: %u bit\n", sizeof(float)*8, sizeof(double)*8);
printf("FLT_MAX = % e\n", FLT_MAX);
printf("FLT_MIN = % e\n", FLT_MIN);
printf("DBL_MAX = % e\n", DBL_MAX);
printf("DBL_MIN = % e\n", DBL_MIN);
float f1 = 3.14159265358979323846; // float
double d1 = 3.14159265358979323846; // double
printf("Value of f1 (float): %.20f\n", f1);
printf("Value of d1 (double): %.20lf\n", d1);
return 0;
}
[実行結果] ----------------------- float: 32 bit, double: 64 bit FLT_MAX = 3.402823e+038 FLT_MIN = 1.175494e-038 DBL_MAX = 1.797693e+308 DBL_MIN = 2.225074e-308 Value of f1 (float): 3.14159274101257324219 Value of d1 (double): 3.14159265358979311600
(1) プログラムでは分数式のまま記述することができないので、割り算で代用することになる。その場合に分子・分母に括弧をつけなければならなくなるので注意すること。
たとえば、\(\displaystyle \frac{a}{b}\) は a/b でよいが、\(\displaystyle \frac{a+b}{c}\) は a+b/c ではなく、(a+b)/c のようにカッコを付ける。
同様に、\(\displaystyle \frac{a}{b+c}\) は a/b+c ではなく a/(b+c)、\(\displaystyle \frac{a}{bc}\) は a/b*c ではなく a/(b*c) となる。
(2) 整数どうしの割り算は整数除算を行うので、実数除算にするためには分子か分母を実数化する必要がある(※プログラミング入門Iでも説明)。整数値を実数化するには小数点をつければよい。
#include <stdio.h>
int main(void)
{
double x1, x2, x3, x4;
x1 = 1 / 2; /* 整数除算 */
x2 = 1./ 2; /* 分子を実数化 */
x3 = 1 / 2.; /* 分母を実数化 */
x4 = 1./ 2.; /* 分子・分母を実数化 */
printf("x1 = %f, x2 = %f, x3 = %f, x4 = %f\n", x1, x2, x3, x4);
return 0;
}
[実行結果] ----------------------- x1 = 0.000000, x2 = 0.500000, x3 = 0.500000, x4 = 0.500000 -----------------------
上記の他に整数変数を実数化するための方法としては、キャスト演算子(double)などを用いた型変換がある。以下のコードを実行して、計算結果を確認してみよう。
#include <stdio.h>
int main(void)
{
int a=1, b=2;
double x1, x2, x3, x4;
x1 = a / b; /* 整数除算 */
x2 = (double)a / b; /* 分子を実数化 */
x3 = a / (double)b; /* 分母を実数化 */
x4 = (double)a / (double)b; /* 分子・分母を実数化 */
printf("a = %d, b = %d\n", a, b);
printf("x1 = %f, x2 = %f, x3 = %f, x4 = %f\n", x1, x2, x3, x4);
return 0;
}
[実行結果] ----------------------- a = 1, b = 2 x1 = 0.000000, x2 = 0.500000, x3 = 0.500000, x4 = 0.500000 -----------------------
関数をグラフで表すと、xとyの関係を理解する大きな手助けとなる。このため、数値解析では一定間隔に刻んだ数列を用いて計算することが多い。以下は、柴田望洋「新版 明解C言語 入門編」の第7章にある、 0 から1 まで 0.01 間隔の実数列を生成するプログラム List 7-9 を少し手直ししたものである。
/*
0 から1 まで 0.01 間隔の実数列を生成する。
*/
#include <stdio.h>
int main(void)
{
float x;
for (x=0; x<=1; x+=0.01) printf(" x = %g\n", x);
return 0;
}
[実行結果] ----------------------- x = 0 x = 0.01 x = 0.02 ... x = 0.979999 x = 0.989999 x = 0.999999 -----------------------
このように、実数(単精度浮動小数点数)を直接、for文で増減させると、刻み幅(上記の場合は0.01)を2進数にしたときの丸め誤差(表現誤差)が累積されて次第に値を正確に刻んでくれなくなる場合が多い。このようなことを避けるためにも、for文は必ず整数で制御するべきである。
/*
0 から1 まで 0.01 間隔の実数列を生成する。(ループを整数で制御)
*/
#include <stdio.h>
int main(void)
{
int i;
float x;
for (i=0; i<=100; i++) {
x = i / 100;
printf(" x = %g\n", x);
}
return 0;
}
上記のプログラムは、前述の考え方を適用した「明解C言語」の第7章 List 7-10とほぼ同じものであるが、整数除算になっているため0しか出力されない。以下の実行結果になるように修正しよう。
[実行結果] ----------------------- x = 0 x = 0.01 x = 0.02 ... x = 0.98 x = 0.99 x = 1 -----------------------