我的任务是为在long double
上运行的计算器编写代码。事实证明,在某一点之后,计算器会失去精确度。例如,它正确计算9999999 ^ 2
,但就99999999 ^ 2
而言,它会产生结果,该结果比应该的结果大1。我已经读过浮点精度的东西,但所有问题都与小数有关。我知道可以通过使用gmp库来修复它,但我的问题是:
还有其他方法可以解决这个问题吗? 如何计算/显示长双精度计算器准确的点?
的main.c
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;
}
evalexpression.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;
}
}
calcfilter.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;
}
答案 0 :(得分:3)
使用pow(f1, f2)
会产生效果,因为在调用double
之前将参数转换为pow()
并解释了OP的问题。 double
通常很难从16位十进制挖掘开始,如在OP的情况下。建议powl()
// return (pow(f1, f2));
return (powl(f1, f2));
其他说明:
long double
可以使用LDBL_DIG
十进制数字精确编码数字。你的机器上至少有10个,可能还有18个。
long double
,打印时需要输出有效数字LDBL_DECIMAL_DIG
,以便将该数字与其他long double
区分开来。要查看所有重要性,请在尝试确定与精确度相关的问题时避免使用%Lf
。
printf("%.*Le", LDBL_DECIMAL_DIG - 1, x); // "%Le"
另见Printf width specifier to maintain precision of floating-point value
[edit]浮点何时失去精度?
* /
很简单。只要结果不是那么小(次正常)或溢出,答案应该是0.5 ULP(unit in the last place)。
+ -
当结果远离零时,再次失去0.5 ULP。但是当像1.0 - 0.9999999...
这样的取消时,几乎所有精度都会丢失。
z=pow(x,y)
是一个很大的数字并且函数写得不好时,例如当数学标识被简单地使用时,{p> z
可能非常不精确:z = exp(log(x)*y)
。否则,良好的pow()
结果将在1.0 ULP内。
@M.M comments使用powl()
的替代方法是#include <tgmath.h>
,以便自动正确选择多个<math.h>
函数。