user3033514
user3033514

Reputation: 87

Strange conversion from float to int

for(float i = 40; i <= 42; i+=0.1f)
{
    float offset = (i-40) * 10 * sizeof(float);
    int wtf = offset;
    printf("%.1f =?= %d\n", offset, wtf);
    fseek(fin, offset, SEEK_SET);
    fread(&tf, sizeof(float), 1, fin);
    //printf("%.1f %.4f\n", i, tf);
}

Output:

0.0 =?= 0 4.0 =?= 3 8.0 =?= 7 12.0 =?= 11 16.0 =?= 15 20.0 =?= 19 24.0 =?= 23 28.0 =?= 27 32.0 =?= 31 36.0 =?= 35 40.0 =?= 39 44.0 =?= 43 48.0 =?= 47 52.0 =?= 51 56.0 =?= 55 60.0 =?= 59 64.0 =?= 63 68.0 =?= 67 72.0 =?= 71 76.0 =?= 75 80.0 =?= 79

I dont understand this, can you help me?

Upvotes: 1

Views: 210

Answers (3)

Matt
Matt

Reputation: 20786

0.1 expressed in base 2 is

0.0001100110011001100110011001100110011...

The 0011 in the fraction repeats infinitely, just as 3 does in 1/3=0.3333333... base 10. Since actual float values can only hold a finite number of digits, there will necessarily be an error in the approximation.

The only fractional numbers that can be precisely expressed with a finite number of digits in base 2 are those rational numbers whose denominator is a whole power of 2. So for example, replacing 0.1f with 0.125f (=1/8)

for(float i = 40; i <= 42; i+=0.125f)
{
    float offset = (i-40) * 10 * sizeof(float);
    int wtf = offset;
    printf("%.1f =?= %d\n", offset, wtf);
}

results in values with no truncation error:

0.0 =?= 0
5.0 =?= 5
10.0 =?= 10
15.0 =?= 15
20.0 =?= 20
25.0 =?= 25
30.0 =?= 30
35.0 =?= 35
40.0 =?= 40
45.0 =?= 45
50.0 =?= 50
55.0 =?= 55
60.0 =?= 60
65.0 =?= 65
70.0 =?= 70
75.0 =?= 75
80.0 =?= 80

This is because 0.125 base 10 is precisely equal to 0.001 base 2.

Upvotes: 0

Raxvan
Raxvan

Reputation: 6505

when i = 40.1f offset will be 3.99993896 which is not exactly 4. For your example you can use std::round(offset) if you use c++11 standard or if round is not available std::floor(offset + 0.5f);

Floating points can't represent all the values in the universe and when in some cases the math on papers is correct on floating points it might not be.

Upvotes: 1

Mike Seymour
Mike Seymour

Reputation: 254461

The problem is that 0.1 isn't exactly representable as a binary floating-point number. Each float value is slightly less than you expect, and conversion to int rounds down, giving a value one less than you expect.

You can get the expected values by rounding to the nearest integer:

int wtf = std::round(offset);

Upvotes: 3

Related Questions