1) 浮動小数点との等号比較を行おうとしています。一部の浮動小数点形式は機能しますが、IEEE 形式は機能しません。等しい比較はできません。その float を int に変換してから、int 比較を行う必要があります。整数の場合 (ここでは 32 ビットなどに限定されません)、各数値を表す方法は 1 つしかないため、等号比較を行うことができます。
2) 浮動小数点演算は基数 2 であり、基数 10 の処理を要求していることを思い出してください。そのため、変換の問題、切り捨てが発生します。また、IEEE を使用していると仮定します。つまり、3 つの丸めモード (基数 2) があるため、それにも対処する必要があります。ある種の double_to_integer((double*1000.0)+0.5) を実行して、それらを比較する必要があります。うまくいかないコーナーケースを見つけても驚かないでしょう。
この問題に関するさらに興味深い情報。この方法での共用体の使用は C 標準ではサポートされていませんが、たまたま機能することが多いことに注意してください...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
double trunc(double d)
{
return (d>0) ? floor(d) : ceil(d) ;
}
int comparedigits(float a , float b)
{
if (trunc(1000.0 * a) == trunc(1000.0 * b))
{
return 1;
}
return 0;
}
union
{
unsigned int ul;
float f;
} fun;
union
{
unsigned int ul[2];
double d;
} dun;
int main ( void )
{
float g;
float h;
int t;
g = 2.346;
h = 2.34599;
t = comparedigits(g,h);
printf("%u\n",t);
printf("raw\n");
fun.f=g; printf("0x%08X\n",fun.ul);
fun.f=h; printf("0x%08X\n",fun.ul);
dun.d=g; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=h; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0F * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0F * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("floor\n");
dun.d=floor(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=floor(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("ceil\n");
dun.d=ceil(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=ceil(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("%u\n",(unsigned int)(g*1000.0));
printf("%u\n",(unsigned int)(h*1000.0));
if (trunc(1000.0F * g) == trunc(1000.0F * h))
{
printf("true\n");
}
else
{
printf("false\n");
}
return(0);
}
コンパイルして実行
gcc test.c -o test -lm
./test
1
raw
0x401624DD
0x401624B3
0x4002C49B_A0000000
0x4002C496_60000000
trunc
0x40A25200_00000000
0x40A25200_00000000
trunc
0x40A25400_00000000
0x40A25200_00000000
floor
0x40A25200_00000000
0x40A25200_00000000
ceil
0x40A25400_00000000
0x40A25400_00000000
2345
2345
false
したがって、二重計算ではなく単一計算で 1000 * x を実行すると、問題が解決するようです
1000.0 * a は混合モードです。1000.0 は、単精度と指定されていない限り、C 標準では倍精度です。a は single であるため、a は double に変換され、数学は double として実行され、double 関数に渡されます。1000.0F は単数、a は単数なので、乗算は単数演算として行われ、次に倍精度に変換されます。したがって、おそらく本当の問題は、g と h を double に変換して丸めることにあります。仮数の違いをもっと掘り下げる必要があります...
鍵はこれだと思います、double 掛ける single 1000.0 * x 結果
trunc
0x40A25200_00000000
0x40A25200_00000000
それらが等しい場合、同じ数を何をしても同じ結果になります。シングル倍のシングルをダブルに変換すると、それらは異なります。
trunc
0x40A25400_00000000
0x40A25200_00000000
これにより、コードが機能します(これらの2つの特定の値に対して)。
false