我正在尝试在 GNU C 中创建一个返回 32 位浮点数绝对值的 fabs 函数。我有三种不同的方式,分别称为 fabs1、fabs2 和 fabs3:
#include <math.h>
#include <stdio.h>
typedef union
{
float v;
struct
{
int mantissa : 23;
int exponent : 8;
int negative : 1;
} b;
} components;
float fabs1(float f)
{
return f >= 0.0 ? f : -f;
}
float fabs2(float f)
{
components c;
c.v = f;
c.b.negative = 0;
return c.v;
}
float fabs3(float f)
{
double aux = f;
unsigned short cw;
__asm__
(
"finit;\
fstcw %[cw];\
andw $0xf0ff, %[cw];\
orw $0x0200, %[cw];\
fldcw %[cw];\
fldl %[aux];\
fabs;\
fstpl %[aux];"
: [aux] "=mr" (aux) : "m" (aux), [cw] "m" (cw)
);
return aux;
}
void main(void)
{
printf("fabs(-189.55f) = %f\n", fabs(-189.55f));
printf("fabs1(-189.55f) = %f\n", fabs1(-189.55f));
printf("fabs2(-189.55f) = %f\n", fabs2(-189.55f));
printf("fabs3(-189.55f) = %f\n", fabs3(-189.55f));
}
共有三种不同的函数,一种使用简单的决策,一种使用联合,稍微复杂一些,最后一种使用 x86 汇编。我正在 Cygwin 32 位中编译它:
C:/Developer/Cygwin/bin/i686-w64-mingw32-gcc -masm=att -I.. -std=c99 -o main.exe main.c
我在Windows 11中运行它,结果是:
fabs(-189.55f) = 189.550000
fabs1(-189.55f) = 189.550003
fabs2(-189.55f) = 189.550003
fabs3(-189.55f) = 189.550003
但他们确实应该是:
fabs(-189.55f) = 189.550000
fabs1(-189.55f) = 189.550000
fabs2(-189.55f) = 189.550000
fabs3(-189.55f) = 189.550000
你能发现差异吗?在这三种情况下如何去掉多余的 0.000003?
基本问题是数字
189.550000
无法在 float
中表示到该精度级别 - 最接近的值是 189.5500030517578125
,当以小数点后 6 位数字打印时为 189.550003
因此,当您使用
fabs
(返回 double
)时,您会得到值 189.55000000000001136868377216160297393798828125
(双精度可以获得的最接近的值),但所有函数都返回 189.5500030517578125
的浮点值