1

我的任务是为在long double. 事实证明,在某个点之后,计算器会失去精度。例如,它计算正确9999999 ^ 2,但就99999999 ^ 2它产生的结果而言,它比它应该的大 1。我已经读过一些关于浮点精度的内容,但所有问题都与小数有关。我知道可以通过使用 gmp 库来修复它,但我的问题是:

有没有其他方法可以解决这个问题?
如何计算/显示长双精度计算器准确的点?

主程序

int main(int argc, char *argv[])
{
    char str[100];
    char key[] = "exit";

    if (argc > 1)
    {
        switch (argv[1][1])
        {
            case 'V': case 'v':
                setValid(1);
                break;
            case 'E': case 'e':
                setError(1);
                break;
            default:
                setValid(1);
                break;
        }
    }
    else
        setValid(1);

    while (gets(str) != NULL && strcmp(key, str) != 0)
    {
        if (calcFilter(str));
        else
            printf("expression error!\n");
    }

    return 0;
}

评估表达式.c

static long double f1, f2;
static char op;

int isValidExpression(const char *str)
{
    int res;
    char ops[10];

    res = sscanf(str, "%Lf %s %Lf", &f1, ops, &f2);

    if (res == 3 && (ops[0] == '+' || ops[0] == '*' || ops[0] == '-' ||
        ops[0] == '/' || ops[0] == '^'))
    {
        op = ops[0];
        return 1;
    }
    else
        return 0;

long double getExprValue(void)
{
    switch (op)
    {
        case '+':
            return (f1+f2);
        case '*':
            return (f1*f2);
        case '-':
            return (f1-f2);
        case '/':
            return (f1/f2);
        case '^':
            return (pow(f1, f2));
        default:
            perror("something went wrong");
            return 0;
    }
}

计算过滤器.c

static int errorsw = 0, validsw = 0;

int calcFilter(const char *str)
{
    if (validsw == 1 && isValidExpression(str))
    {
        printf("%s = %Lf\n", str, getExprValue());
        return 1;
    }
    else if (errorsw == 1 && !isValidExpression(str))
    {
        printf("%s\n", str);
        return 1;
    }
    else if (errorsw)
        return 1;
    else
        return 0;
}

void setError(int mode)
{
    errorsw = mode;
}

void setValid(int mode)
{
    validsw = mode;
}  
4

1 回答 1

3

使用pow(f1, f2)会适得其反,因为它会double在调用之前将参数转换为pow()并解释 OP 的问题。 double通常在 OP 的情况下从 16 位十进制数字开始会遇到麻烦。推荐powl()

// return (pow(f1, f2));
return (powl(f1, f2));

其他注意事项:

long doubleLDBL_DIG可以用十进制数字精确编码数字。你的机器上至少有 10 个,可能有 18 个以上。

long double, 打印时需要LDBL_DECIMAL_DIG输出的有效数字以将该数字与所有其他数字区分开来long double。要查看所有意义,请避免%Lf在尝试确定与精度相关的问题时使用。

printf("%.*Le", LDBL_DECIMAL_DIG - 1, x);   // "%Le"

另请参见Printf 宽度说明符以保持浮点值的精度


[编辑] 浮点何时失去精度?

* /简单。只要结果不是那么小(不正常)或溢出,答案应该是 0.5 ULP(最后一个单位)。

+ -当结果远离零时,再次损失高达 0.5 ULP。但是当有类似的取消时1.0 - 0.9999999...,几乎所有的精度都会丢失。

z=pow(x,y)z当数字很大并且函数写得不好 时可能非常不精确,例如当数学恒等式被简单地使用时: z = exp(log(x)*y). 否则,好的pow()结果将在 1.0 ULP 之内。


@MM 评论说,使用的替代方法powl()#include <tgmath.h>自动正确选择许多<math.h>功能。

于 2015-12-16T22:53:56.813 回答