【发布时间】:2017-06-12 09:25:53
【问题描述】:
尝试使用 C 上的 GMP 库创建一个简单的精确计算器,在测试我的程序时遇到了一个相当意外的错误; 2 + 2.22222 = 4.22221999999999999999。
这是我在 GMP 中使用的函数;
mpf_add(res, f1, f2); //with res as result already initilized, f1 and f2 as the values
关于打印功能;
gmp_printf("%.Ff\n", getExprValue()) //with getExprValue the calculating function that has the line above.
编辑:
这是完整的代码;
int isValidExpression(const char *str) // to check the input
{
int res;
char validOps[6] = {'+', '-', '*', '/', '^', '\0' };
mpf_init(f1);
mpf_init(f2);
// RES = 3 WHEN SYNTAX OK
res = gmp_sscanf(str, "%Ff %c %Ff", f1, &op, f2);
// RES = 4 WHEN OPERAND OK
if(strchr(validOps, op)) res++;
int exprCounter = 0;
char* token = strtok(str, " +-*/^");
while(token != NULL)
{
// TOO MANY WORDS IN THE EXPRESSION
++exprCounter;
if(exprCounter > 2) res = 0;
// TAKE THE NEXT WORD
token = strtok(NULL, " +-*/^");
}
return (res==4);
}
mpf_t* getExprValue() //Working on the operand
{
static mpf_t res;
mpf_init(res);
switch(op)
{
case '+':
mpf_add(res, f1, f2);
break;
case '-':
mpf_sub(res, f1, f2);
break;
case '*':
mpf_mul(res, f1, f2);
break;
case '/':
mpf_sgn(f2) != 0 ? mpf_div(res, f1, f2) : printf("Division by ");
break;
case '^':
mpf_pow_ui(res, f1, mpf_get_si(f2));
break;
}
return res;
}
至于主要的;
char input[MAX_INPUT];
while(gets(input))
{
if(strcmp(input, "exit") != 0)
{
isValidExpression(input) ? gmp_printf("%.Ff\n", getExprValue()) : puts("Expression error");
}
else
{
puts("Exiting...");
break;
}
}
【问题讨论】:
-
如果您能给我们一个完整的代码示例,这可能会有所帮助...
-
@unwind 没有问题。不过,输入类型会很有趣。 OP 应该知道浮点并不总是 100% 准确,无论它有多少精度。
-
32位浮点数只能存储
2**32不同的值,一个double只能存储2**64不同的值,这仍然是事实。无论您为此付出多大的意义,它仍然适用。浮点数始终是范围和精度之间的权衡。准确表示它们的一种方法是存储 两个 整数值来表示分数。 -
十进制值 2.22222 不能用二进制浮点数精确表示,即使是无限精度。出于同样的原因,1/3 不能精确地表示为小数。这不是精确度的问题,而是表示的问题。您可能想阅读speleotrove.com/decimal
-
不要在任何新项目中使用 GMP 的 mpf_t,它被 MPFR 的 mpfr_t 取代。他们的网页也有几个十进制数库的链接。如果您的编译器支持,您也可以考虑像 _Decimal64 这样的类型。根据您的目标,Rationals 也可能是一种选择。
标签: c calculator precision addition gmp