4 ms·
What does "unions that allow access to the wrong field" mean?
by ajkjk 11y ago
What does "unions that allow access to the wrong field" mean?
- pcwalton 11y agounion foo { int x; float y; }; union foo bad; bad.x = 100; printf("%f\n", bad.y); // undefined behavior (though usually works)
- SomeCallMeTim 11y agoWhich is actually a useful feature in some obscure optimizations, like this fast (approximate) square root: float half_r=r*0.5F; union { float y; int32_t i; }; y=r; i=0x5f375a86-(i>>1); y=y*(1.5F-(half_r*y*y)); return y;
- Scaevolus 11y agoThat hasn't been an optimization for 15 years now. Fast approximate inverse square root is a builtin operation on most FPUs-- on x86, it's RSQRTSS, which tends to be the same cost as a floating point multiply. (for Haswell, it has a 5 cycle latency, and 1 cycle reciprocal throughput)
- SomeCallMeTim 11y agoInteresting, though not useful unless there's a good way to access it from portable C/C++. An __asm__ section or equivalent would work, but would only be useful on a particular CPU -- and different compilers use different syntax for embedding assembly, so that's not ideal either. But where I'd more likely use that optimization at this point is on Arm or ATmega processors. ARM doesn't seem to have an approximate inverse square root, based on a quick check, and ATmega are frequently still stuck with software floating point, so I'd hardly say that the optimization is dead.
- JoshTriplett 11y agoIf you want to reinterpret a float as an integer or vice versa, you can do that easily enough with Rust's unsafe functions: fn approx_invsqrt(r : f32) -> f32 { let y : f32 = unsafe { let i : i32 = std::mem::transmute(r); std::mem::transmute(0x5f375a86 - (i>>1)) }; return y*(1.5-(0.5*r*y*y)); } fn main() { println!("approx_invsqrt(2.0) = {}", approx_invsqrt(2.0)); } Result: approx_invsqrt(2.0) = 0.70693
- kzrdude 11y agoAnd Rust specifies their floats to be IEEE 754, so it always works (endianness may apply though).
- JoshTriplett 11y agoSeems reasonably safe to assume that i32 and f32 have the same endianness.
- kerny 11y agoNo undefined behavior. Perfectly legal since C99 standard.
- _wmd 11y agoCan we please get a quote on this one. If reinterpreting memory via a union is valid in C99, including data vs function pointers, then so would reinterpreting that memory via a cast, which would seem to violate one of the most elementary aspects of the standard (e.g. such a rule would be difficult or impossible to implement on a Harvard architecture machine, which the standard previously made plenty of allowance for)
- pcwalton 11y agoApparently that is correct and I was wrong above (though not in C++) [1]. However, according to the standard, the actual value is unspecified, because float bit representation is undefined. [1]: http://dbp-consulting.com/tutorials/StrictAliasing.html http://dbp-consulting.com/tutorials/StrictAliasing.html
- kzrdude 11y agoPage 83, section §6.5.2.3 of the C11 standard, footnote 95) says > 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’’). This might be a trap representation
- veddan 11y agoGoing through a union is the correct way of viewing one type as another. Casting pointers to accomplish this is undefined behaviour (except for char *) and compilers do TBBA (type-based alias analysis) based on the assumption you don't do it. Famously, the Linux kernel does this a lot and needs to be compiled with -fno-strict-aliasing.
- comex 11y agoThe C standard does not allow casting directly between function pointers and other pointers; they might not even be the same size. Using a union or memcpy will allow you to standards-compatibly reinterpret the bit pattern of a function pointer as a data pointer or vice versa (modulo size differences), but creating the resulting pointer, even without dereferencing it, might cause a crash if the bit pattern is a "trap representation", and in any case dereferencing it isn't guaranteed to do anything useful. ...Not that a portable program has any business trying to read instruction opcodes in the first place.
- Tomte 11y agoIn C you can store a value to a field of a union and then read from a different field. This is usually done to "get the byte representation of a float" or things like that. Except... it's undefined behavior.
- kerny 11y ago> Except... it's undefined behavior. No longer true since C99.
- sinistersnare 11y agoSource please? Or do I need money to get a copy of the standard?
- kzrdude 11y agoGet publicly available draft 1570 of the C11 standard from the committee: http://www.open-std.org/jtc1/sc22/wg14/www/standards.html http://www.open-std.org/jtc1/sc22/wg14/www/standards.html
- tibiapejagala 11y agoLatest standard drafts are usually free for C99 see http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
- rylee 11y agohttps://news.ycombinator.com/item?id=9827417 https://news.ycombinator.com/item?id=9827417
- deleted 11y ago[deleted]