Swemoph
Swemoph

Reputation: 326

Compensating for double/float inaccuracy

I've written a math calculator that takes in a string from the user and parses it. It uses doubles to hold all values involved when calculating. Once solved, I then print it, and use std::setprecision() to make sure it is output correctly (for instance 0.9999999 will become 1 on the print out.

Returning the string that will be output:

//return true or false if this is in the returnstring.

if (returnString.compare("True") == 0 || returnString.compare("False") == 0) return returnString;

    //create stringstream and put the answer into the returnString.
    std::stringstream stream;
    returnString = std::to_string(temp_answer.answer);

    //write into the stream with precision set correctly.
    stream << std::fixed << std::setprecision(5) << temp_answer.answer;


    return stream.str();

I am aware of the accuracy issues when using doubles and floats. Today I started working on code so that the user can compare the two mathematical strings. For instance, 1=1 will evaluate to true, 2>3 false...etc. This works by running my math expression parser for each side of the comparison operator, then comparing the answers.

The issue i'm facing right now is when the user enters something like 1/3*3=1. Of course because i'm using doubles the parser will return 0.999999as the answer. Usually when just solving a non-comparison problem this is compensated for at printing time with std::setprecision() as mentioned before. However, when comparing two doubles it's going to return false as 0.99999!=1. How can I get it so when comparing the doubles this inaccuracy is compensated for, and the answer returned correctly? Here's the code that I use to compare the numbers themselves.

bool math_comparisons::equal_to(std::string lhs, std::string rhs)
{
    auto lhs_ret = std::async(process_question, lhs);
    auto rhs_ret = std::async(process_question, rhs);
    bool ReturnVal = false;

    if (lhs_ret.get().answer == rhs_ret.get().answer)
    {
        ReturnVal = true;
    }

    return ReturnVal;
}

I'm thinking some kind of rounding needs to occur, but i'm not 100% sure how to accomplish it properly. Please forgive me if this has already been addressed - I couldn't find much with a search. Thanks!

Upvotes: 0

Views: 265

Answers (2)

Tsunn
Tsunn

Reputation: 23

First consider:

As a basic rule of thumb, a double will be a value with roughly 16dp where dp is decimal places, or 1.0e-16. You need to be aware that this only applies to numbers that are less than one(1) IE for 10.n you'll have to operate around that fact you can only have 15dp EG: 10.0e-15 and so on... Due to computers counting in base 2, and people counting in base 10 some values can never be properly expressed in the bit ranges that "most" modern OS use.

This can be highlighted by the fact that expressing 0.1 in binary or base 2 is infinitely long.

Therefore you should never compare a rational number via the == operator. Instead what the "go to" solution conventionally used is:

You implement a "close enough" solution. IE: you define epsilon as a value eg: epsilon = 0.000001 and you state that if (value a - value b) < epsilon == true. What we are saying is that if a - b is within e, for all intents and purposes for our program, its close enough to be regarded as true.

Now for choosing a value for epsilon, that all depends on how accurate you need to be for your purposes. For example, one can assume you need a high level of accuracy for structural engineering compared to a 2D side scrolling platform game.

The solution in your case you be to replace line 7 of you code:

(lhs_ret.get().answer == rhs_ret.get().answer)

with

abs(lhs_ret.get().answer - rhs_ret.get().answer) < epsilon where abs is the absolute value. IE ignoring the sign of the value lhs.

For more context i highly recommend this lecture on MIT open courseware which explains it in an easy to digest manner.

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00sc-introduction-to-computer-science-and-programming-spring-2011/unit-1/lecture-7-debugging/

Upvotes: 1

Frank Puffer
Frank Puffer

Reputation: 8215

Assuming that answer is a double, replace this

lhs_ret.get().answer == rhs_ret.get().answer

with

abs(lhs_ret.get().answer - rhs_ret.get().answer) < TOL

where TOL is an appropriate tolerance value.

Floating point numbers should never be compared with == but by checking if the absolute difference is less than a given tolerance.

There is one difficulty that needs to be mentioned: The accuracy of doubles is about 16 decimals. So you might set TOL=1.0e-16. This will only work if your numbers are less than 1. For a number with 16 digits, it means that the tolerance has to be as large as 1.

So either you assume that your numbers are smaller than say 10e8 and use a relatively large tolerance like 10e-8 or you need to do something much more complicated.

Upvotes: 2

Related Questions