5 ms·
This line: > x = (float)&i; breaks strict aliasing, and has undefined behavior. If you compile it with g++ -O2, you may get unexpected results. The solution
by boas 14y ago
This line:
> x = (float)&i;
breaks strict aliasing, and has undefined behavior. If you compile it with g++ -O2, you may get unexpected results. The solution is to use a union, or to compile with -no-strict-aliasing.
I used to have a lot of code like this, which worked fine on older compilers, but not on newer compilers. It's perfectly reasonable code, so I'm not sure why the newer compilers don't produce the expected behavior by default.
- mistercow 14y ago>I'm not sure why the newer compilers don't produce the expected behavior by default. The reason is that there are optimizations that a compiler can do when it knows that two particular pointers will not reference the same location in memory at a given time (which is called aliasing). These optimizations have to do with memory access. If pointers cannot be proven not to alias each other, then the order in which they are loaded, modified, and stored will be very rigid since it is impossible to tell if changing the order will change the end result. Sometimes that's just life; you might have some complex interactions going with your pointers, and it may actually be that the order of operations can't be changed. But if you can assume that the pointers don't alias each other, then the compiler is free to change the order of operations for better efficiency. It can often remove unnecessary loads and stores (if x and y don't alias, you don't have to reload y just because you changed x), and it can also put loads together at the beginning and stores together at the end (which is faster because, um, caching? I'm not really clear on that part). The catch is that in general, there is no way for the compiler to know whether two pointers can alias each other. In C99, you can inform the compiler of this with the restrict keyword, but that requires that you do some careful analysis of your code to make sure you aren't lying to the compiler. If you mess up, you may end up with bugs that are very difficult to track. So that's where strict aliasing comes in. Usually, two pointers of different types won't point at the same memory location. So they added the "strict aliasing" rule to make that fact official, because it means that in the vast majority of cases, code gets a nice speedup without any changes or difficult reasoning. Using a union is basically a way of informing the compiler that you need it to make an exception this one time. I used to use a macro something like this to make the process concise: #define PUN(x, toType) (((union {__typeof__(x) __a; toType __b;})x).__b) Then you'd do: int i = PUN(int, x); // evil floating point bit level hacking i = 0x5f3759df - (i >> 1); // what the fuck? x = PUN(float, i); x = x*(1.5f-(xhalf*x*x));
- premchai21 14y agoI actually use macros along the lines of: #define LOAD(TYP, ptr) (*((TYP *)memcpy((TYP[1]){ }, ptr, sizeof(TYP)))) #define REINTERPRET(AS_TYP, FROM_TYP, val) LOAD(AS_TYP, (FROM_TYP[1]){ val }) In practice (that I've found, with GCC), the memcpy and single-use temporaries get optimized away entirely. In strict C99, writing one member of a union and then reading a different member of the same union is undefined in the general case, last I checked. http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html http://cellperformance.beyond3d.com/articles/2006/06/underst... seems to agree, but suggests that every major compiler recognizes it as a de facto idiom and supports it anyway.
- mistercow 14y agoYep, type punning through a union is nonstandard and unportable, but if I'm not mistaken, it is a documented feature in GCC.
- stephencanon 14y agoType punning through a union is explicitly defined to work in C99 and C11 (footnote 95 in C11): > If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). Please don't help spread the myth that compiler writers can break this idiom. That said, I use memcpy in my own code.
- premchai21 14y agoAll right, I see it in C99 as well now (§6.5.2.3 footnote 82, if I'm not mistaken). Thanks for the correction.
- mistercow 14y agoOh, well I stand corrected.
- kmm 14y agoI don't think you can use unions. In the draft of the ISO-IEC 9899 standard (C99) I have, paragraph 6.2.6.1.7 (I'm not kidding) says > When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values. So no undefined behaviour (nasal demons and such) but still unspecified.
- raverbashing 14y agoI suppose this is saying something like this union X{ char a; float b; } If you store something in a, the other 3 bytes corresponding to b are unspecified But I suspect this doesn't apply if a and b have the same size.
- stephencanon 14y agoThis was corrected in C99.TC2 with the addition of footnote 82 (footnote 95 in C11).