On 15 avr, 17:47, gpderetta <gpdere...@gmail.com> wrote:
> On Apr 15, 3:14 pm, ciccio <nos...@thismail.com> wrote:
> > I was wondering what the main reason is why reinterpret_cast
> > fails to work as expected when using optimizations. Here is
> > the simple example code which fail to give the correct
> > result when optimizing.
> > #include <iostream>
> > int main() {
> > long D1 = 0xbcfbc4f0d9b65179;
> > long D2 = 0xbcfbc4f0d9b65042;
> > std::cout.setf(std::ios::showbase | std::ios::showpos);
> > std::cout.setf(std::ios::hex,std::ios::basefield);
> > std::cout.setf(std::ios::scientific,std::ios::floa tfield);
> > std::cout.setf(std::ios::internal,std::ios::adjust field);
> > std::cout.precision(20);
> > double E1 = *reinterpret_cast<double *> (&D1);
> > double E2 = *reinterpret_cast<double *> (&D2);
> > std::cout << E1 << "\t" << D1 << std::endl;
> > std::cout << E2 << "\t" << D2 << std::endl;
> > return 0;
> > }
> > This gives me the output when compiled without optimization flags :
> > $ g++ foo.cpp
> > $ ./a.out
> > -6.16602326664765606567e-15 0xbcfbc4f0d9b65179
> > -6.16602326664741072993e-15 0xbcfbc4f0d9b65042
> > while compiled with optimization (-O3)
> > $ g++ -O3 foo.cpp
> > $ ./a.out
> > +0.00000000000000000000e+00 0xbcfbc4f0d9b65179
> > -6.16602326664741072993e-15 0xbcfbc4f0d9b65042
> > Although this is only a minor difference here, it becomes ludricous when
> > doing some simple math operations.
From a quality of implementation point of view, I'd consider
that a bug, in this particular case. The compiler can see the
reinterpret_cast, and should turn off all pointer related
optimizations because of it.
The standard, however, is very clear that accessing an object
via an lvalue which is neither the type of the object, nor a
character type, is undefined behavior. And I wouldn't complain
if the compiler had problems when the reinterpret_cast was not
immediately visible, say because it was in another function.
[...]
> Using reinterpret cast to read from a variable with a
> different type than the one used to store into it is undefined
> behaviour (check on the standard the exact .
Not always. There are two issues which need to be addressed:
-- First, the standard says that the result of the conversion
(using reinterpret_cast) is "unspecified. All that is
required is that if he casts it back to the original type,
he get the same value (because in this case, the alignment
requirements are the same). Other language in the standard
suggests, however, that reinterpret_cast should behave in a
manor unsurprising to someone familiar with the addressing
architecture of the machine---in other words, his
expectations aren't unrealistic.
In practice, I wouldn't expect this to work unless
everything was taking place in the same function. There's
too much to be gained in optimization by assuming that two
pointers to different types aren't aliases.
-- The second is the fact that accessing an object other than
through an lvalue with its type, or as an array of character
type, is undefined behavior. The motivation behind this, of
course, is that anything but character types can contain
trapping values---if the bit pattern in his long, for
example, corresponded to a trapping NaN, I wouldn't expect
such code to work.
From a quality of implementation point of view, again, I
would expect it to work provided the bit patterns were in
order. Modulo the business about undetectable aliasing, of
course.
> Some compilers support type punning via an union as an
> extension.
Historically, I believe that this was the preferred way; at
least, it was what I was taught in C, in the early 1980's. The
C standard (and the C++ standard, following that) makes it quite
clear, however, that this is NOT an allowable practice.
> The only 'portable' way to do that is to use memcpy:
> memcpy(&B, &A, sizeof(float). (and making some assumptions
> about int and float sizes and representations, of course).
Yes. The memcpy works, of course, because 1) you're going
through a void*, which means that all of the casts are
static_casts (although arguably, using reinterpret_cast should
give the same results), and 2) because memcpy copies the
underlying bytes, i.e. it uses an unsigned char* to do the
copying.
Practically, from a quality of implementation point of view, I'd
say that if the reinterpret_cast doesn't work when all of the
operations (including the reinterpret_cast) are visible in the
function, then it's a compiler error. But in practice, the need
for such type punning, except in low level code to access the
underlying raw memory, is so rare that there's no point worrying
about it.
--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34