admin管理员组文章数量:1026373
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:
- employ a precise float library
- maybe switch to a larger data type (double) for a wider range of accuracy
- use integer based math
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.
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:
- employ a precise float library
- maybe switch to a larger data type (double) for a wider range of accuracy
- use integer based math
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.
3 Answers
Reset to default 3The 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.
Common float
is 32-bits, so float
can only encode about 232 different values. Text can represent unlimited number of values, so a reasonable expectation is that only a subset is possible to achieve OP's goal. @Pete Becker
doing such calculation for all floats?
Yes, if input does not use too many significant decimal digits.
0.0399999991 has 9 significant decimal digits.
With common float
, all decimal text versions of a value in the [-FLT_MAX ... FLT_MAX]
range that uses FLT_DIG
(e.g. 6) or fewer significant decimal digits will convert to a float
via stof()
, that when printed, using the same number of significant decimal digits, will present the same. This float
may differ a little in value from the text, yet will match (round) when not too many significant decimal places are printed.
Floating point numbers are (usually) represented in binary, which can not represent decimal fractions exactly (except for a few ones), so the round trip "textual decimal representation --> binary floating point --> textual decimal representation" will fail most of the time.
What you can do is to e.g. printf in C to the wanted number of decimals. If you need exactly two decimals, use integers expressed in hundreths.
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:
- employ a precise float library
- maybe switch to a larger data type (double) for a wider range of accuracy
- use integer based math
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.
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:
- employ a precise float library
- maybe switch to a larger data type (double) for a wider range of accuracy
- use integer based math
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.
-
4
There are an infinite number of numeric values, but only a finite number of
float
values; you cannot represent every possible numeric value in afloat
. Nor in adouble
or in any numeric type you might invent so long as the memory on your system is finite. – Pete Becker Commented Nov 16, 2024 at 18:49 - 5 Re: "this results in a value of 0.0399999991" -- no, it results in a value that, when converted to text by some unspecified mechanism, becomes "0.0399999991". Don't confuse the text representation of a value with the actual floating-point value. – Pete Becker Commented Nov 16, 2024 at 18:56
-
5
0.0399999991
is more accurate than0.0400000010
, why do you want the less accurate value? – Alan Birtles Commented Nov 16, 2024 at 19:04 -
2
If you are dealing with money, instead of using a floating point type use a 64 bit integer and store the value in cents. Then
cents / 100
is dollars andcents % 100
is the cents. Then you don't have to worry about floating point inaccuracies. – NathanOliver Commented Nov 16, 2024 at 19:19 - 2 This question is similar to: Is floating-point math broken?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – Richard Critten Commented Nov 16, 2024 at 20:02
3 Answers
Reset to default 3The 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.
Common float
is 32-bits, so float
can only encode about 232 different values. Text can represent unlimited number of values, so a reasonable expectation is that only a subset is possible to achieve OP's goal. @Pete Becker
doing such calculation for all floats?
Yes, if input does not use too many significant decimal digits.
0.0399999991 has 9 significant decimal digits.
With common float
, all decimal text versions of a value in the [-FLT_MAX ... FLT_MAX]
range that uses FLT_DIG
(e.g. 6) or fewer significant decimal digits will convert to a float
via stof()
, that when printed, using the same number of significant decimal digits, will present the same. This float
may differ a little in value from the text, yet will match (round) when not too many significant decimal places are printed.
Floating point numbers are (usually) represented in binary, which can not represent decimal fractions exactly (except for a few ones), so the round trip "textual decimal representation --> binary floating point --> textual decimal representation" will fail most of the time.
What you can do is to e.g. printf in C to the wanted number of decimals. If you need exactly two decimals, use integers expressed in hundreths.
本文标签: cCan I convert a string to float with a minimum decimal point precision valueStack Overflow
版权声明:本文标题:c++ - Can I convert a string to float with a minimum decimal point precision value? - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745647119a2161090.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
float
values; you cannot represent every possible numeric value in afloat
. Nor in adouble
or in any numeric type you might invent so long as the memory on your system is finite. – Pete Becker Commented Nov 16, 2024 at 18:490.0399999991
is more accurate than0.0400000010
, why do you want the less accurate value? – Alan Birtles Commented Nov 16, 2024 at 19:04cents / 100
is dollars andcents % 100
is the cents. Then you don't have to worry about floating point inaccuracies. – NathanOliver Commented Nov 16, 2024 at 19:19