1

So I just found this bug in my code and I am wondering what rules I'm not understanding.

I have a float variable logDiff, that currently contains a very small number. I want to see if it's bigger than a constant expression (80% of a 12th). I read years ago in Code Complete to just leave calculated constants in their simplest form for readability, and the compiler (XCode 4.6.3) will inline them anyway. So I have,

            if ( logDiff > 1/12 * .8 ) {  

I'm assuming the .8 and the fraction all evaluates to the correct number. Looks legit:

(lldb) expr (float) 1/12 * .8
(double) $1 = 0.0666666686534882
(lldb) expr logDiff
(float) $2 = 0.000328541

But it always wrongly evaluates to true. Even when I mess with enclosing parens and stuff.

(lldb) expr logDiff > 1/12 * .8
(bool) $4 = true
(lldb) expr logDiff > (1/12 * .8)
(bool) $5 = true
(lldb) expr logDiff > (float)(1/12 * .8)
(bool) $6 = true

I found I have to explicitly spell at least one of them as floats to get the correct result,

(lldb) expr logDiff > (1.f/12.f * .8f)
(bool) $7 = false
(lldb) expr logDiff > (1/12.f * .8)
(bool) $8 = false
(lldb) expr logDiff > (1./12 * .8f)
(bool) $11 = false
(lldb) expr logDiff > (1./12 * .8)
(bool) $12 = false

but I recently read a popular style guide explicitly eschew these fancier numeric literals, apparently according to my assumption that the compiler would be smarter than me and Do What I Mean.

Should I always spell my numeric constants like 1.f if they might need to be a float? Sounds superstitious. Help me understand why and when it's necessary?

4

4 回答 4

4

The expression 1/12 is an integer division. That means that the result will be truncated as zero.

When you do (float) 1/12 you cast the one as a float, and the whole expression becomes a floating point expression.

于 2013-08-14T08:13:57.007 回答
1

In C int/int gives an int. If you don't explicitly tell the compiler to convert at least one to a float, it will do the division and round down to the nearest int (in this case 0).

I note that the linked style guide actually says Avoid making numbers a specific type unless necessary. In this case it is needed as what you want is for the compiler to do some type conversions

于 2013-08-14T08:16:49.117 回答
1

An expression such as 1 / 4 is treated as integer division and hence has no decimal precision. In this specific case, the result will be 0. You can think of this as int / int implies int.

Should I always spell my numeric constants like 1.f if they might need to be a float? Sounds superstitious. Help me understand why and when it's necessary?

It's not superstitious, you are telling the compiler that these are type literals (floats as an example) and the compiler will treat all operations on them as such.

Moreover, you could cast an expression. Consider the following:

float result = ( float ) 1 / 4;

... I am casting 1 to be a float and hence the result of float / int will be float. See datatype operation precedence (or promotion).

于 2013-08-14T08:17:56.400 回答
1

That is simple. Per default, a numeric value is interpredted as an int. There are math expresssions where that does not matter too much. But in case of divisions it can drive you crazy. (int) 1 / (int) 12 is not (float) 0.08333 but (int) 0. 1/12.0 would evaluate to (float) 0.83333.

Side note: When you go for float where you used int before there is one more trap waiting for you. That is when ever you compare values for equality.

float f = 12/12.0f; 
if (f = 1) ... // this may not work out. Never expect a float to be of a specific value. They can vary slightly. 

Better is:

if (abs(f - 1) < 0.0001) ... // this way yoru comparison is fuzzy enough for the variances that float values may come with.  
于 2013-08-14T08:23:01.267 回答