1

I have a string

strValue = "11.900000"

I want to parse it to a float value. So I tried:

float fValue = -1;
sscanf_s(strValue.c_str(), "%f", &fValue);

try
{
    double test = std::stod(strValue, &sz);
    fValue = (float) std::stod(strValue, &sz);
}
catch (...)
{
    fValue = -1;
}

For double it is accurate:

test = 11.900000000000000

For float it is inaccurate:

fValue = 11.8999996

Is there a possibility to do this accurately? (I know there is a inaccuracy while working with floats, but is there a better way to do it?)

phuclv
  • 37,963
  • 15
  • 156
  • 475
lalelu
  • 103
  • 9
  • 4
    No. There is no way to represent 11.9 as a `float`, as your example illustrates. The underlying value is [binary-based](https://en.wikipedia.org/wiki/IEEE_floating_point) and (most) multiples of 0.1 do not have an exact representation in binary. – Phylogenesis Jun 22 '15 at 07:48
  • Floating point numbers *are* inaccurate and suffer from scaling errors. Your compiler may support a `decimal` type but C++ still hasn't standardized decimal support - it's scheduled for C++17. – Panagiotis Kanavos Jun 22 '15 at 07:53
  • You may be able to use the [Boost MultiPrecision](http://www.boost.org/doc/libs/1_53_0/libs/multiprecision/doc/html/boost_multiprecision/intro.html) library until decimals are added to C++ – Panagiotis Kanavos Jun 22 '15 at 07:57
  • It's hard to understand what you're asking because it's not clear what you're actually trying to do. You clearly found a more accurate way, using `double`. So why are you asking us how to do something you already figured out how to do? (How accurate do you want it to be exactly? Infinitely accurate?) – David Schwartz Jun 22 '15 at 08:00
  • @DavidSchwartz even doubles aren't sufficient for financial calculations if there is a chance that you'll get errors in 4 or 5 decimal calculations. That's why other languages have `decimal` types – Panagiotis Kanavos Jun 22 '15 at 08:08
  • 1
    @PanagiotisKanavos: that's not necessarily true... many financial systems use `double` and trust that all the accumulated errors won't reach the level of impacting the least significant digit remaining after rounding to cents/yen/whatever. With ~15 significant digits, that's often practical even if there're errors *"in 4 or 5 decimal calculations"*. – Tony Delroy Jun 22 '15 at 08:15
  • @TonyD only if their language doesn't have a decimal type, or they want to trade accuracy for hw-accelerated speed (in which case they use floats or ints). When that fails of course ... google for [Knight Capital](https://en.wikipedia.org/wiki/Knight_Capital_Group#2012_stock_trading_disruption). There's a difference between accounting where an error in even a single transaction is an issue, and trading of large amounts where errors typically get cancelled out. Eg, a single dollar error is enough for an airline to refuse to issue a ticket bought in another currency – Panagiotis Kanavos Jun 22 '15 at 08:31
  • @PanagiotisKanavos: *"only if their language"* - only? Even the [C++17 proposal](http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3871.html) says *"It isn’t expected that decimal floating points are used for heavy number crunching..."*. *"trade accuracy for hw-accelerated speed (in which case they use floats or ints)"* - nonsense - many financial systems use `double` over `float` because it provides *sufficient* accuracy while `float` doesn't. As someone who's spent the last ~20 years programming for banks & financial data providers, your summary of industry practice doesn't ring true. – Tony Delroy Jun 22 '15 at 09:05
  • 11.9 can no way be represented exactly in a binary double type. Print it with at least 20 digits of precision and you'll see – phuclv Jul 16 '15 at 10:37

2 Answers2

2

The value 11.8999996 is the most precise approximation of 11.9 that can be represented by the float type.

dlask
  • 8,776
  • 1
  • 26
  • 30
2

It's not correct for double or any other binary floating-point types. Just print more digits after the radix point and you'll begin to see "garbage" digits soon

The only way for you is to use a decimal floating-point type. If your platform has FLT_RADIX == 10 then you can directly use the built-in float, but obviously it's not your case, and I don't know any current system that uses decimal float except IBM z-series.

So you need a custom type for that. You can use some IEEE-754 decimal32 libraries like Intel® Decimal Floating-Point Math Library or libdfp. Or if you use gcc then it already has support for decimal float via the _Decimal32 type

You can find more information in this question How do I use decimal (float) in C++?

phuclv
  • 37,963
  • 15
  • 156
  • 475