int = int * float unexpected syntax rule

Given the varibles int a, int b one would have assumed that
a = b * 0.1f

… would have assigned a to be 1/10 of b. For some reason or other, it doesn’t.

This works instead:
a = (int)(b * 0.1f)

Is this a c++ thing or is it a bug?

Tried the following with gcc 4.3.2 and ‘-Wall -Wextra -pedantic’.

int a=10, b;

b = a * 0.1f;

cout << b << endl; // == 1

Maybe a Bug in CUDA, because ‘-pedantic’ forces ISO-C/ISO-C++ usage.

What does it assign a to be?

My initial guess as to what is happening is that .1f is not exactly representable as a float, and the closest representation is just slightly smaller that .1, so that when you multiply 10 * (.1f) you end up with something just slightly smaller than 1 which will get truncated to 0 when casting back to an int.

However, it appears that .1f in float is actually 0.10000000149012 at least as chosen by gcc 4.3.2, or slightly bigger than .1, so that wouldn’t explain it afterall.

Then I tried the following program

#include <iostream>

#include <iomanip>

int main(void)

{

   int a = 10, b;

   b = a * .1f;

   std::cout << std::setprecision(14) << .1f << std::endl << b << std::endl;

   return 0;

}

And got the following output (identical with and without the pedantic flag) on gcc 4.3.2 executed directly and with nvcc:

0.10000000149012

1

So, I can’t replicate the problem. I haven’t tried running the code ON the gpu, perhaps there is a difference there. Does emulation mode produce the same results for you?

@Tigga
b is in the range from hundreds of thousands to almost a billion

@eelsen
No I have not tried emulation and I actually expect the results to be in line with those of gcc - after all, that is what nvcc does anyway.

The actual working expression is:

int oscDelta = 0x42400F; // FIXME
oscState += oscDelta + ((int) (((float)oscDelta) * (((float)tx)+1.0f) * 0.005525f));

… where oscState and oscDelta are integers and tx is threadId.x. Without the typecasts, oscDelta ends up being zero.

This gives the effect of floor(b*0.1). If b is 10, the result is unstable (if 0.1 is internally represented as 0.999999 => 0)

If you want it rounded to the closest int as with round() you need to:

a = (int)(b * 0.1 + 0.5); if b>=0

a = (int)(b * 0.1 - 0.5); if b<0

To avoid edge rounding effect. So you will have “unpredictable” results for b= 5,15,…

I do also have a fixed point logic alternative, avoiding slow and unstable float-int conversions:

floor:

a = ((b<<4)/10) >>4 if b>=0

in this way, if b=10, it will give always 1.

round:

a = ((b<<4)/10 + 8) >>4 if b>=0

a = ((b<<4)/10 - 8) >>4 if b<0

If you are wondering, “why <<4?”, it is because 2^4 is the smallest int larger than 10. Take care to overflows, with this alternative.

Regards,

Sigismondo