float型

How to Use ffmpegXメモ (ばる氏運営) の2006年05月31日付の記事『mp4boxがfps指定値を地味に守らない件 — その3』に「-fps 23.976025」という数値があったので、それについて少し考察してみました。
ご存知と思いますが、動画にはフレームレートという数値があります。
1秒間に何フレーム (コマ) 表示するかという値で、単位はfps (Flame Per Second) です。
通常、日本のアニメは24枚/secで表現されています。
そのため、フレームレートも本来なら24fpsとなります。
ところが、日本の放送で採用しているNTSC規格では24fpsの場合は24000/1001で規定されているため、23.976023976023976…という値になります。
小数点以下6桁までの精度で使いたければ23.976024となります。
ただ、この数字ではいろいろと不都合があるため、10進法で割り切れる23.976fpsを規定値として採用されています。
動画などで「23.976fps」という表記があるのはこのためですが、この23.976という数字は結構厄介な数字だったりします。
詳しくはHow to Use ffmpegXメモの当該記事にも載っているリンクを辿って妖精現実様の記事をお読みになっていただくとわかると思います。

さて、10進法では23.976という数値で収まりますが、コンピュータが使用している2進法ではそうはいきません。
まず、2進法では

24000 101110111000000
1001 000001111101001
となります。
これをWindows標準搭載の電卓 (関数電卓モード) などで計算するとわかりますが、101110111000000 / 1111101001 = 10111、すなわち10進数の23となります。
つまり、小数点以下は2進法では求めることができません。
しかし、実際のプログラムでは小数点以下も計算されています。
これは浮動小数点を利用することで大きな値を利用することができるからです。
例えばC言語では変数宣言にfloat (浮動小数点) 型を指定してあげると、小数精度6桁の演算が可能となります。
また、double (倍精度浮動小数点) 型の場合は小数精度10桁の演算が可能となります。

実はここに23.976025という値の正体が隠されていると私は考えています。
本来、小数点以下6桁で有効桁を取るなら23.976024とならなければならないのは上でも記載した通りですが、float型で演算を行うと23.976025という数値が出てきます。

Sylphide@PC-TX26GS-A ~/src/C
$ ./double.exe
1st Number: 24000
2nd Number: 1001
24000.0000000000 / 1001.0000000000 = 23.9760239760


Sylphide@PC-TX26GS-A ~/src/C
$ ./float.exe
1st Number: 24000
2nd Number: 1001
24000.0000000000 / 1001.0000000000 = 23.9760246277

上はdouble型で計算したときの結果、下はfloat型で計算したときの結果です。
ご覧になればわかると思いますが、float型では小数第6位から値がおかしくなっています。
これは小数精度6桁だからです。
つまり6桁以下は精度は補償されていません。
そしてこの算出された値、23.9760246277という値を小数点以下6桁で丸めてください。
23.976025となります。
おそらくこれが23.976025の値の正体と私は考えているわけです。
もちろん、あくまで推測の域なのは言うまでもありませんが。
 
ついでなので今回使用したプログラムのソースも載せておきます。
2つ載せるとスペース思いっきり使うのでfloat型のほうだけ載せておきます。
なお、double型の場合はfloatdoubleに、%f%lfに変更するだけです。(printf()のところは%fでも動きます。)

#include <stdio.h>
#include <stdlib.h>    //atof()関数に必要

int main(void)
{
 char num1[32],num2[32];
 float a,b,ans;
 
 /* 1つ目の数値を入力させる */

 printf("1st Number: ");
 fgets(num1,sizeof(num1),stdin);
 a = atof(num1);   
//数値変換
 

 
/* 2つ目の数値を入力させる */
 
printf("2nd Number: ");
 fgets(num2,sizeof(num2),stdin);
 b = atof(num2);   
//数値変換
 

 
/* 演算 */
 
ans = a / b;    //a / bを実行し、ansに代入
 
 
/* 結果出力 */
 printf("%.10f / %.10f = %.10f\n",a,b,ans);
 
 return 0;
}

%.10fという表記は、浮動小数点型で小数点以下10桁を表示させるためのものです。
基本は%fですが、.10をつけることにより小数第10位まで表示されるようになります。
間違えて%10fと入力してしまうと、小数点より上の数が10桁表示されるので注意してください。
ソースコードがなぜTabでインデントされていないかは、あえて突っ込まないでください。
HTMLで書くのがだるかったからコピーしたらそうなっただけですので。 (ぉぃ
scanf()ではなくfgets()を使用しているのは、scanf()だとfloat型で宣言しているにもかかわらず数値以外が入力された場合に、とんでもないことになる場合があるからです。
また、除算エラー対策をしていないのは、単に上のことを調べたかっただけなのであえてしておりません。
除算エラー対策はいずれまた書きます。
なお、このプログラムはMinGW のgcc.exe (GCC 3.4.5 mingw special) で動作を確認しております。
コマンドラインはcmd.exeのインターフェースがどうも味気なく見えるのでCygwin (bash) を利用してます。

実行環境は Windows XP Home Edition with SP2 (win32 x86)、CPUはCeleron D 2.66GHzです。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中