I'm working on something that extracts a float from a string. I noticed that certain values result in slightly inaccurate float values. Some research on the subject pretty much has told me to either:
I would prefer not having to do any of these (especially that last one), but instead create a function that "rounds" a float up until it matches the string representation. I did some some basic math and realized that this did happen (at least in my example).
In my example, I had a string with the value "0.04"
. When I use std::stof()
on it, this results in a value of 0.0399999991
. The minimum amount I have to add to that float value for it to match the string (at least until my string's decimal point) is 0.0000000019
so it then converts to 0.0400000010
. I figured this out by just adding until the shift happened.
Is there an already existing method of doing such a calculation for all floats? If not, I'm worried I would have to do a loop to keep increasing by 0.0000000001
. However, I'm not sure how I can programmatically conclude success given that switching 0.0399999991
back to a string results in a string of "0.04"
again.
The answer depends to some extent on what you are trying to do with the numbers after you extract them from the string.
If you want to print them out for human consumption later, or if you want to embed them in other strings that get written out in some other format, and if it's important that you print out the value exactly as input, no matter what, then you might not want to convert to float
, or double
, at all. If there's no need to do math on them, you might as well not convert to a numeric type at all, but rather carry them around as strings.
But if you have some math you need to do on them, it will be easiest if you use one of C++'s native floating-point types, except: they're going to have this problem.
You can minimize — and in many case, eliminate — the problem by printing your numbers out at the end (or converting them to strings) with the precision limited to something that is reasonable for your numbers. For example, in the example that you gave, if you were to print the value f
back out using
std::cout << std::fixed << std::setprecision(2) << f;
then you would see 0.04
as you expect.
(See How to set the output precision to 2 decimal places in C++ for more on this technique.)
But you can not, repeat not, "fix" this problem by adding 0.0000000001, or anything like that. The problem is not that atof
wrongly converted the string "0.04"
to the floating-point value 0.0399999991. The problem is that types float
and double
can not represent the value 0.04 exactly, at all, period. If atof
couldn't store the value 0.04 into a floating-point variable, then you can't do so by adding 0.0000000009, either.
The fundamental issue is that floating-point values in C++ (and most languages) are represented internally in binary, which can't represent multiples of 0.1 or 0.01 exactly. (This is similar to the way we can't exactly represent the fraction 1/3 in decimal.). See the canonical SO questions Why are floating point numbers inaccurate and Is floating-point math broken for more (much more) on this topic. The bottom line is that when you convert the decimal value 0.04 to a single-precision float
, the closest you can get is a binary fraction which is equivalent to the decimal fraction 0.039999999105930328369140625. As a double
, the closest you can get is 0.040000000000000000832667268468867405317723751068115234375. You just can't get any closer than that.
In some languages — but not C or C++ — the default method for converting floating-point values for output employs a reasonably sophisticated algorithm to automatically, in effect, round to an appropriate number of digits when printing, which would tend to give you exactly what you want in cases like these, without your having to set the precision explicitly. There's probably a decent way to utilize this algorithm (known as "Errol") in a C++ program, but I don't know what it is.