1

I have the following code in my application whereby I need to parse a string and convert it to double

int i = ((QString)"294.4").toDouble() * 100;

I am supposed to see the value 29440 as a value of i. However, every time I do so, I get the value of 29439. I am not sure why it is happening as it does not happen always. e.g. 294.1,294.2,294.3 works fine. 294.5 also works fine.

I understand that interpreting floating point numbers should be an issue and undetermined behavior can be expected but this one seems to be a very simple case.

Can someone please guide me on how to reliably parse the values so that it always gives me the exact value represented by its string equivalent?

Murtuza Kabul
  • 6,438
  • 6
  • 27
  • 34

1 Answers1

3

It is not an issue of Qt, it is a matter of the internal representation of floating point numbers (base-2 based, not base-10 based). See Why not use Double or Float to represent currency? for some good background information.

If you rely on exact values, do not use floating point numbers, but store the integer and the fractional part of your number in separate integers, e.g.

int integ = 294;
int fraction = 4;   /* assumption: one fractional digit */

and then use integer arithmetic only for your calculations.

See Is there a library class to represent floating point numbers? for links to various existing libraries for these kind of calculations.


Based on the feedback from the comments, I suggest to parse the String into the integer and fractional part, for example like this:

struct Money {
    int integ;
    int fract;
};

Money parseMoney(const QString& input) {
    QString cleanInput = input;
    Money result = {0,0};

    // remove group separators
    QLocale locale = QLocale::system();
    cleanInput.remove(locale.groupSeparator());

    // convert to money
    QStringList parts = cleanInput.split(locale.decimalPoint());
    if (parts.count() == 1) {
       result.integ = parts[0].toInt();
    } else if (parts.count() == 2) {
       result.integ = parts[0].toInt();
       result.fract = parts[1].toInt() * 10;
    } else {
         // error, not a number
    }

    return result;
}

The assumption is that the input is a string which is formatted according to the current locale, e.g. if the group separator is ',' the decimal point is '.' and the input string is "3,294.4" the result in Money would be {3294, 40}.

Community
  • 1
  • 1
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • I understand it. Main issue here is, it is user who is providing the value. In this case, it is currency and price of something which I cannot ask user to put in as integer. What I am looking for is just a method to reliably parse the value. Once parsed perfectly, it works fine for me. – Murtuza Kabul Oct 16 '12 at 12:14
  • I also understand that it is not the issue with Qt but a general problem. – Murtuza Kabul Oct 16 '12 at 12:14
  • You get the value as a String? – Andreas Fester Oct 16 '12 at 12:17
  • 1
    exactly and thats the issue. Trying to parse the string to double results in inexact value – Murtuza Kabul Oct 16 '12 at 12:17
  • In any case, you must avoid a conversion to a floating point type. Please see my edited answer for a possible approach. – Andreas Fester Oct 16 '12 at 12:49
  • Do you mean to say that Decimal type is more appropriate for parsing currencies? Further, I am not using groups so it is not an issue for me. I have tried the same example in c#, php, java and all of them were able to correctly parse the value except of C++ and specifically QString class toDouble implementation. I do a lot of parsing and what you have suggested will make the application very inefficient. Can you suggest me a proper data type or method to directly parse it ? – Murtuza Kabul Oct 16 '12 at 12:56
  • Yes. Decimal types / fixed point types are the only valid approach when working with currencies. Never use floating points for that. I tried your sample with Java and it also prints me 29439 instead of 29440 (the IEEE-754 standard used is the same). Surely my example provided above is not designed for efficiency - it can surely be made more efficient :-), e.g. by directly iterating the characters of the string in a loop. – Andreas Fester Oct 16 '12 at 13:13