4

Trying to create a simple precise calculator using GMP library on C, I came upon a rather unexpected error while testing my program; 2 + 2.22222 = 4.22221999999999999999.

Here is the function I am using from GMP;

mpf_add(res, f1, f2); //with res as result already initilized, f1 and f2 as the values

and as for the printing function;

gmp_printf("%.Ff\n", getExprValue()) //with getExprValue the calculating function that has the line above.

Edit :

Here is the code in its integrity;

    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;
}

and as for the main;

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;
    }
}
  • 3
    It could be helpfull, if you can give us a full example of your code... – mame98 Jan 26 '17 at 19:36
  • I get a similar result when working with `float`, less bad with `double`. For a calculator project please use `double` if you are not already doing so. – Weather Vane Jan 26 '17 at 19:41
  • @WeatherVane OP is already using GMP, which is the [GNU Multiple precision library](https://gmplib.org/), so the code shouldn't be limited to the small ranges of `float` and `double`. Which is why I de-duped this question. – unwind Jan 26 '17 at 20:19
  • 1
    @unwind no prob with that. The input types would be interesting though. OP should know that floating point cannot always be 100% accurate, no matter how much precision it has. – Weather Vane Jan 26 '17 at 20:21
  • a float value of 2.22222 should be exact and should be represented exactly by a float, so I don't see why it would produce that. Maybe try using gmp_printf with both the inputs and outputs - maybe that will give a clue. – Jerry Jeremiah Jan 26 '17 at 21:12
  • @mame98 Edited there goes my full code – Saad Mehdaoui Jan 26 '17 at 21:43
  • @WeatherVane The precision of floats is exactly the reason why I opted for GMP Arbitrary precision library and using the GMP's functions the precision should not be a matter anymore – Saad Mehdaoui Jan 26 '17 at 21:45
  • 1
    It is still a fact that a 32-bit float can only store `2**32` different values, a `double` only `2**64` different values. No matter how much significance you go for this will still apply. Floating point numbers are always a trade-off between range and precision. One way to accurately represent them is by storing **two** integer values to represent a fraction. – Weather Vane Jan 26 '17 at 21:51
  • Where are `f1` and `f2` defined? Please post a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) . – Haldean Brown Jan 27 '17 at 00:03
  • 1
    The decimal value 2.22222 cannot be exactly represented in binary floating point, even with infinite precision. It's for the same reason that 1/3 cannot be exactly represented as a decimal. It is not an issue of precision, but of the representation. You may want to read http://speleotrove.com/decimal/ – casevh Jan 27 '17 at 02:54
  • 1
    Don't use GMP's mpf_t for any new project, it is replaced by MPFR's mpfr_t. Their webpage also has links to several libraries for decimal numbers. You could also consider a type like _Decimal64 if your compiler supports it. Rationals may also be an option depending on your goals. – Marc Glisse Jan 27 '17 at 08:23
  • @HaldeanBrown mpf_init(f1); mpf_init(f2); – Saad Mehdaoui Jan 28 '17 at 12:33
  • @MarcGlisse I will try it and be back to you :D – Saad Mehdaoui Jan 28 '17 at 12:43

1 Answers1

2

The problem is you are trying to decimal arithmetic and using floating point numbers. You need to use a decimal arithmetic library to do exact calculations with decimal numbers.