1

In C++ Qt Library there is a function QRect screenGeometry(). QRect has functions int width() and int height(). If I save result of this functions to int variables then division works well, but if I use one of this function directly in division there are strange results:

QRect scr_size = QApplication::desktop()->screenGeometry();
int a = 1280;
int b = 720;
int c = scr_size.width();
int d = scr_size.height();
qDebug() << c; // 1920
qDebug() << d; // 1080
qDebug() << ((float(a) / b) - (float(c) / d)); // 0
qDebug() << ((float(a) / b) - (float(scr_size.width()) / d)); // 1.32455e-08
qDebug() << ((float(a) / b) - (float(c) / scr_size.height())); // 1.32455e-08
qDebug() << ((float(a) / b) - (float(scr_size.width()) / scr_size.height())); // 1.32455e-08

Why? And how to fix it?

user2160982
  • 189
  • 1
  • 2
  • 10
  • what if you try: ((float(a) / float(b) - (float(c) / float(d)); Maybe this will shed some light: http://stackoverflow.com/questions/6003198/c-float-division-and-precision – shish Aug 28 '14 at 09:32
  • for me the code above gives: 1920 1200 0.177778 0.177778 0.177778 0.177778 – Ferenc Deak Aug 28 '14 at 09:32
  • 3
    Floating point calculations are not exact. http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – PaulMcKenzie Aug 28 '14 at 09:34
  • @fritzone it is because your resolution is not 1920x1080 – user2160982 Aug 28 '14 at 09:34
  • Is it release or debug? Try to change it! (to see interesting difference) – Ilya Aug 28 '14 at 09:34
  • @PaulMcKenzie but what is the difference between int variable and function, returning int? Why results are different? – user2160982 Aug 28 '14 at 09:35
  • @Ilya release and debug have similar results for me – user2160982 Aug 28 '14 at 09:36
  • 1
    @user2160982 - Do not be surprised at any result you get from doing floating point calculations. There is a wealth of information on SO about floating point calculations and precision. In addition to SO, you have this: http://www.parashift.com/c++-faq/floating-pt-errs.html – PaulMcKenzie Aug 28 '14 at 09:37
  • What is the platform and compiler? – stefaanv Aug 28 '14 at 09:40
  • @stefaanv mingw 4.8.2 g++ windows – user2160982 Aug 28 '14 at 09:42
  • 1
    @user2160982 - `float(c)` and `float(scr_size.width())` during your division tests are different in terms of what is going on low-level. One is a variable, the other is a function call -- two totally different ways of getting the result. If you need convincing, generate the assembly listing and you will more than likely see that the value comes from doing different FP operations. Maybe even more relevant: http://www.parashift.com/c++-faq/floating-point-arith2.html – PaulMcKenzie Aug 28 '14 at 09:45
  • @PaulMcKenzie thanks. interesting link – user2160982 Aug 28 '14 at 09:51
  • What do you need to fix? In terms of screen pixels, 1E-8 is good enough to be zero. Instead of dwelling on such imaginary issues, tell us what your real problem is. Most likely you're doing something with the floating point values that you're not supposed to do, like comparing them exactly. I'm referring to the other code you don't show, the source of the real problem. The "issues" you show here are just the reality of a finite precision arithmetic. As is many times the case, you're not telling us what the real problem is, only throwing up a non-working attempt at a solution. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:07
  • -1 Because there's no problem to solve, nothing to fix. – Kuba hasn't forgotten Monica Aug 28 '14 at 13:10

3 Answers3

5

Obviously as stated by many users you are hitting problems related with floating point precision and compiler dependant optimization/choice on how to encode the requested operations.

The key is in the qrect::width() and qrect::height() implementation: they are both inlined, that means that after the preprocessor the line:

((float(a) / b) - (float(scr_size.width()) / d))

will be:

((float(a) / b) - (float(scr_size.x2-scr_size.x1+1) / d))

that left to the compiler how to encode in a efficent way the formula in object code.

This is why in all the lines where you call member function of qrect you get a different result.

For your information: never compare this kind of difference with 0, is a common mistake, please simple check that the absolute value ( fabs(x) ) is less than a very small number ( 0.0001 for example ).

Stefano Buora
  • 1,052
  • 6
  • 12
2

but what is the difference between int variable and function, returning int? Why results are different?

The compiler's free to do several things that could - by themselves of in combination - explain the difference in values:

  • evaluate the full expression in arbitrary order, decisions about which could well be affected by having to make a function call (even if it ends up being inline)

    • order of evaluation can change the way errors accumulate (or even occasionally cancel each other out)
  • change decisions on when to store the temporary results during evaluation in higher precision registers, such as by using an 80 bit CPU register, then pick different times to round back to 32 bit float

  • evaluate things involving constants itself using completely different optimisations, CPU instructions/registers etc. from those used in the code the compiler generates for your program, which means moving from a constant like 1920 to a function call deemed to be knowable only at runtime can delay processing and produce completely different results.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
1

Substracting both values and check if 0 is the same as testing for equalty.

Due to decimal->binary conversion and precision limits operations with floating point numbers are not exact and then is a mistake test for exact equality.

Something that could help to spot the problem is looking at the e-08 in the strange result. 1 * 10^-8 is not 0 but is near 0. Changing notation:

1.32455e-08 is 0,0000000132455 which is not 0 but is very near to 0

float is 32bits in your platform. Using doubles could make the problem disapear. And that will be dangerous because it will be hiding the error but not fixing it.

davidaf
  • 351
  • 1
  • 4