C 和 x86 汇编中的自制“fabs”函数

问题描述 投票:0回答:1

我正在尝试在 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?

c assembly x86 floating-point constants
1个回答
0
投票

基本问题是数字

189.550000
无法在
float
中表示到该精度级别 - 最接近的值是
189.5500030517578125
,当以小数点后 6 位数字打印时为
189.550003

因此,当您使用

fabs
(返回
double
)时,您会得到值
189.55000000000001136868377216160297393798828125
(双精度可以获得的最接近的值),但所有函数都返回
189.5500030517578125

的浮点值
© www.soinside.com 2019 - 2024. All rights reserved.