46

Is it safe to assume that the condition (int)(i * 1.0f) == i is true for any integer i?

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • I suppose it is, because any mantissa bits will be lost. I don't know if `int` gets promoted to `float` however. – Tony The Lion Nov 15 '12 at 15:32
  • 9
    For some reason, I want to say __no__, but I really have no justification for it... :-] –  Nov 15 '12 at 15:32
  • @John: I have the same feeling. I was also thinking about large integers, and whether there's any guarantee about multiplying by `1.0`... probably in IEEE754 the answer is "yes", though. – Kerrek SB Nov 15 '12 at 15:33
  • @John: that was my initial feeling as well, now I just plain don't know :) – Violet Giraffe Nov 15 '12 at 15:34

4 Answers4

73

No.

If i is sufficiently large that int(float(i)) != i (assuming float is IEEE-754 single precision, i = 0x1000001 suffices to exhibit this) then this is false, because multiplication by 1.0f forces a conversion to float, which changes the value even though the subsequent multiplication does not.

However, if i is a 32-bit integer and double is IEEE-754 double, then it is true that int(i*1.0) == i.


Just to be totally clear, multiplication by 1.0f is exact. It's the conversion from int to float that may not be.

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • 6
    To add to this answer: in simple terms the loss of precision is due the fact that float has only 24 bits to accommodate the int, where as an int has 32 bits therefore the least significant bits will be rounded during the cast. – Ifthikhan Nov 15 '12 at 15:52
  • *rounded*, not *discarded*, but yes, exactly. – Stephen Canon Nov 15 '12 at 15:53
  • Agreed, but in reality isn't it discarding? :) – Ifthikhan Nov 15 '12 at 15:55
  • `int(x)` looks like C++ syntax; in C++ the rules are slightly different, and the multiplication is exact, involving conversion from `int` to `double` and from `1.0f` to `1.0` – anatolyg Nov 15 '12 at 15:55
  • 2
    @anatolyg: No. In C++ this is governed by paragraph 10 of section 5 (the *usual arithmetic conversions*): "if either operand is float, the other shall be converted to float." So `i` is converted to `float` before the multiplication, which causes rounding. – Stephen Canon Nov 15 '12 at 15:59
  • Equality is unsafe for floating point numbers for the reasons that Stephen pointed out. There are also more subtle problems with floating point comparisons, for example `if(a <= b) { a = c } if (a > b) { a = d }` giving different results from `if (a <= b) { a = c } else { a = d }`. You would expect the first part of my example to yield a value of `a = c` or `a = d` but it might yield `a = a` because some comparisons fall through. – Michael Shopsin Nov 15 '12 at 16:59
  • 2
    @MichaelShopsin: Exactly one of `a <= b` or `a > b` is true unless `a` or `b` is a NaN. – Eric Postpischil Nov 16 '12 at 02:22
  • @Eric I have seen code where `a <= b` was false and `a > b` was false but `a != NaN`. I had to put in print statements to prove the math problems to my boss since he wrote the loop with the two if statements instead of if else. – Michael Shopsin Nov 16 '12 at 18:16
  • 1
    @MichaelShopsin: You're both right. Eric's statement is absolutely true of floating-point numbers as defined by the standard. However, there have been well documented compiler bugs that produced the behavior that Michael describes. To be clear though, this is not a problem of floating-point comparisons; it's a problem of buggy compilers. – Stephen Canon Nov 16 '12 at 18:28
  • @Stephen I think the problem came from the rounding options in the Absoft F77 compiler which allowed for some strange comparisons to keep compatibility with old code. – Michael Shopsin Nov 16 '12 at 18:31
15

No, IEEE-754 floating point numbers have a greater dynamic range than integers at the cost of integer precision for the same bit width.

See for example the output of this little snippet:

int main() {
        int x = 43046721;

        float y = x;

        printf("%d\n", x);
        printf("%f\n", y);
}

43046721 cannot be represented correctly in the 24 bits of precision available in a 32-bit float number, so the output is something along these lines:

43046721
43046720.000000

In fact, I would expect any odd number above 16,777,216 to have the same issue when converting to a 32-bit float number.

A few points of interest:

  • This has more to do with the implicit int-to-float conversion than with the multiplication itself.

  • This is not by any mean unique to C - for example Java is also subject to the exact same issue.

  • Most compilers have optimization options that may affect how such conversions are handled, by ignoring certain restrictions of the standard. In such a case, (int)((float)x * 1.0f) == x might always be true if the compiler optimizes out the conversion to float and back.

thkala
  • 84,049
  • 23
  • 157
  • 201
1

No, the behavior is implementation defined because C and C++ don't require IEEE-754, even though that is the most common representation by far.

To be sure that IEEE-754 is used:

  • in C, use #ifdef __STDC_IEC_559__
  • in C++, use the std::numeric_limits<float>::is_iec559 constants
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
-5

No it is absolutely wrong for all the integers because of the type cast. check code.

#include <stdio.h>

int main()
{
    int i = 0;
    for (; i < 2147483647; ++i) {
        if ((int)(i * 1.0f) != i) {
            printf("not equal\n");
            break;
        }
    }
    printf("out of the loop\n");
    getchar();
    return 0;
}

This code assumes that you take 32 bit integer

Artur Czajka
  • 18,111
  • 2
  • 28
  • 31
Mohit Sehgal
  • 825
  • 1
  • 9
  • 26
  • 1
    32767 != INT_MAX for 32-bit integers. – Stephen Canon Nov 15 '12 at 15:42
  • 6
    Uh, you *did* run this program, right? Because it actually prints "not equal" on my system, as it should... – thkala Nov 15 '12 at 15:56
  • @thkala It doesn't print "not equal" over here, my system is 32-bit if that matters. –  Nov 15 '12 at 16:26
  • @user9000: what platform and compiler are you using? – thkala Nov 15 '12 at 16:32
  • 2
    @thkala It is **allowed** that floating point computations are carried out at greater precision and range than the type specifies. So it's allowed that the compiler uses `double`s or even `long double` or the extended 80-bit x87 floating point numbers, in which case it's legitimate that "not equal" is not printed. Another (unlikely) possibility is that `float` has at least 31 bits of precision already. But with the standard IEEE754 32-bit `float`s, it **must** print "not equal" if the test is changed to `if ((int)((float)i * 1.0f) != i)`. – Daniel Fischer Nov 15 '12 at 16:41
  • @Cornstalks see above, the implementation has some leeway. – Daniel Fischer Nov 15 '12 at 16:42
  • @Cornstalks yes, I left it running until it printed "out of the loop". –  Nov 15 '12 at 16:43
  • @user9000 Can, but need not. It is definitely not safe to assume that `(int)(i*1.0f)` always round-trips to `i`. Nor is it safe to assume that it doesn't. – Daniel Fischer Nov 15 '12 at 16:51
  • But according to my compiler it is yes. Please help :-( – Mohit Sehgal Nov 15 '12 at 17:05
  • @MohitSehgal The result of one compiler proves nothing. Here, we have the situation that the standard allows both, that `(int)(i * 1.0f) == i` for all `i` of type `int`, and that the equality evaluates to false for some `i`, even for the usual 32-bit two's complement `int`s and the usual 32-bit `float`s with IEEE754 representation (it may evaluate to true for all `i` because the standard explicitly allows the computation `i * 1.0f` to be performed with greater precision [and range] than `float` provides; if the computation is done at `float` precision, you'll get false for some `i`). – Daniel Fischer Nov 15 '12 at 19:57
  • I dont know even after I have edited the answer why people are down voting.?? – Mohit Sehgal Nov 21 '12 at 14:48
  • @MohitSehgal, your edit is actually still wrong. The comparison is not "*absolutely [false] for **all** the integers because of the type cast*". The comparison is only false for integers that require more precision than the mantissa of the float allows. 32 bit IEEE-754 floats have a 24 bit mantissa. All unsigned 32 bit integers between 0x01000000 and 0xFFFFFFFF will make the comparison false. All others will make it true. – kdbanman Dec 21 '15 at 18:12