PHPとはいってもネイティブの浮動小数点数型をそのまま使ってるのは間違いないのでC言語で同じ事やってみた。C言語らしくバイト列のHEXダンプを追加した、以下のようなコードを実行致します。
#include <vcl.h>
#pragma hdrstop
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
void test(double v)
{
unsigned long long *p = (unsigned long long *)&v;
printf("%f\r\n", v);
printf("%llx\r\n", *p);
printf("%d\r\n", (int)v);
}
int _tmain(int argc, _TCHAR* argv[])
{
puts("----");
test(0.201 * 1000);
puts("----");
test(2.01 * 100);
puts("----");
test(20.1 * 10);
puts("----");
test(201);
getchar();
return 0;
}
以下のような結果になるわけです。
----
201.000000
4069200000000000
201
----
201.000000
40691fffffffffff
200
----
201.000000
4069200000000000
201
----
201.000000
4069200000000000
201
やはり「0.201 * 1000」と「2.01 * 100」の結果は、内部的には異なる値になってるようだ。怖いねぇ。怖いから俺寝る。。。では何の解決にもならんので追試してみた。ついでにfloatの場合も試してみることにする。
#include <vcl.h>
#pragma hdrstop
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
int _tmain(int argc, _TCHAR* argv[])
{
puts("----");
double x = 0.201 * 1000;
double y = 2.01 * 100;
unsigned long long *xp = (unsigned long long *)&x;
unsigned long long *yp = (unsigned long long *)&y;
printf("x=%f\r\n", x);
printf("x=%llx\r\n", *xp);
printf("y=%f\r\n", y);
printf("y=%llx\r\n", *yp);
if(x == y)
{
puts("x == y");
}
else
{
puts("x != y");
}
puts("----");
float x2 = 0.201 * 1000;
float y2 = 2.01 * 100;
unsigned long *xp2 = (unsigned long *)&x2;
unsigned long *yp2 = (unsigned long *)&y2;
printf("x2=%f\r\n", x2);
printf("x2=%x\r\n", *xp2);
printf("y2=%f\r\n", y2);
printf("y2=%x\r\n", *yp2);
if(x2 == y2)
{
puts("x2 == y2");
}
else
{
puts("x2 != y2");
}
getchar();
return 0;
}
----
x=201.000000
x=4069200000000000
y=201.000000
y=40691fffffffffff
x != y
----
x2=201.000000
x2=43490000
y2=201.000000
y2=43490000
x2 == y2
やはり「0.201 * 1000 != 2.01 * 100」なのだった。10進でデバッグプリントしてる限り何が悪いのか気づけないのである。浮動小数点数怖い!!なおfloatの場合は人間的な動作となったが、もちろん数値によってはdoubleの201と同じことが起こると考えられる。
浮動小数点数演算で誤差が出るのは当たり前だが、演算結果が「見た感じ同じである」という点で、ちょっとしたワナだよなー思った次第。これが、良くあるパターンで200.999999とかになっててくれれば話は早いのだが。やっぱ寝るか(ぉい