I need a math wiz to help solve this ^^

Pages: 1234
Yes, when you make an union of long double and a single char, BUT, if all members have the same size, all of them are completly initialized when a value is assigned to just one of them! For example, if you have a long double and a char in an union, and you define only the char, 11 out of 12 bytes of the long double will be uninitialized, leading to undefined behaviour. However, if you make an array of 12 characters, and initialize all of them, accessing the long double will have no undefined behaviour! also, if you asign value to the largest member of the union, all of the other members will also be defined, becouse all of their memory space is allocated!
You are arguing the standard.

It is undefined behavior.


The reality is that a lot of compilers implement defined behavior for it, but how they do that may vary. Also, it is still the wrong way to do it because of endianness issues, as already stated.
Just tell me how can that be undefined behaviour if the memory space is allocated?!
PS: We are arguing about unions in somebody else's integer decompiling program topic!
I don't think you understand the term "undefined behaviour".

It does not mean "memory that you have not set a value to".

It means behaviour that cannot be determined by reading the C++ standard (the actual document that defines what C++ is). It could do anything, and that would not be wrong, because the C++ language does not specify what it should do.

For example:

1
2
int x= 7;
int y = ++x + x++; // undefined behaviour 


is undefined behaviour, even though x has a known value at the start.

Edited to remove extreme crazy junk.
Last edited on
the answer is 19
Are you trolling, or do you genuinely not get the point (and presumably don't actually know what C++ is)?
umm, isn't C++ a programming language?
BTW, when you edited it, the answer is 16.
And what defines C++? How do we know that

int x; is correct C++ and
iroueroegjire!!$%%^$%t4365^$%^%$645 is not?

By reading the standard.

What if your compiler does something that the compiler says is wrong? Which is correct C++? Your compiler or the standard?
@Moschops
You do realize by now that viliml is baiting you, right?
We frequently get people here who do not know what a compiler or a linker is. I would not be at all surprised to discover that viliml does not know where C++ comes from.
I was still giving him the benefit of the doubt after his 7:59pm GMT remark, but after the responses to the increment operator stuff removed all doubt for me. No one can know so little about so much when just beginning.
Ok so now I'm confused. I don't fully understand all this "bit shifting" and "undefined behavior". Could someone explain this to me as if I were a 5 year old? haha xD
Forget bitshifting and unions. Here's a mathematical approach to it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
unsigned int CompileInt(int a, int b, int c, int d)
{
    return (a) + (b*256) + (c*65536) + (d*16777216);
}
int DecompileInt(unsigned int Compiled, int id)
{
    for (int i=1; i< id; i++)
        Compiled /= 256;
    return Compiled%256;
}

int main()
{
    unsigned int c = CompileInt(1,2,30,255);
    cout << c << endl;
    cout << DecompileInt(c,1)<<endl;
    cout << DecompileInt(c,2)<<endl;
    cout << DecompileInt(c,3)<<endl;
    cout << DecompileInt(c,4)<<endl;
}

bitshifting is way faster than looped division.
it's also more clear.

1
2
3
4
5
6
7
8
9
10
unsigned CompileInt(int a, int b, int c, int d)
{
  return (unsigned)( (a) | (b<<8) | (c<<16) | (d<<24) );
}

int DecompileInt(unsigned compiled, int id)
{
  id *= 8; // 8 bits per byte
  return (compiled >> id) & 0xFF;
}
Last edited on
Forget bitshifting and unions. Here's a mathematical approach to it.
Emphasis added.

If you are going to give advice, don't tell newbies to ignore good advice for marginal, harder-to-understand advice.

Forget all the expensive multiplications. Use bitshifting.


@SuperSonic
A bitshift just scoots the bits in your value left or right some number. For example, given the value for 0x2B:

    00111011

If you bit-shift right one bit (0x2B >> 1), the right most bit (a one) gets kicked off the end and the others slide right one space. A zero moves in on the left.

    0 -> 0011101 -> 1

    00011101


The right bitshift is a very cheap integer division by two.
The left bitshift is a very cheap integer multiplication by two.


Since we know that each byte of your source is exactly eight bits, we can use it to our advantage when assembling integer values that are bigger than eight bits.

The nice trick about bitshifting is: it doesn't matter how big your integer value is -- as long as you keep shifting bits in, it will work.

Example time:

I want to assemble 0x35 and 0xA1 into an integer. I know that the first byte is the most-significant value, and the second is the least significant. The assembled value should then be 0x35A1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iomanip>
#include <iostream>
using namespace std;

int main()
{
  // Here are our bytes, in order from most-significant to least-significant
  unsigned char bytes[] = { 0x35, 0xA1 };

  // Here is our value. Start with zero.
  int value = 0;

  // Loop through the bytes, from most-significant to least-significant, and
  // shift them into the value.
  for (int n = 0; n < 2; n++)
  {
    // Here we shift-in the byte
    value = value << 8;  // scoot all the bits left 8 places
    value = value | bytes[ n ];  // set the lower-eight bits to the byte value

    // Let's see how it looks right now:
    cout << "value = 0x" << setfill( '0' ) << setw( 8 ) << hex << value;
    cout <<      " = "                                  << dec << value << ".\n";
  }

  return 0;
}

Now that you have something to play with, try adding some bytes to the bytes[] array, and see how it affects the result.

The example I posted earlier shows how to go backwards, and how to deal with different endiannesses. This example used "big-endian", since the more important (significant) bytes were listed first in the array. In "little-endian", the more important (significant) bytes are listed last in the input array.

Hope this helps.
@SuperSonic > I want to be able to compile 8-bit numbers into a full sized integer.

The first version that you posted
1
2
3
4
int CompileInt(int a, int b, int c, int d)
{
    return (a) + (b*256) + (c*65536) + (d*16777216);
}
is almost as good as anything else in this thread.

If we know that multiplication (say by 65536) is more expensive than a bit-wise shift on the particular implementation, we can also be certain that the writer of the compiler is aware of at least that much, and would know how to apply strength-reduction. Since the compiler does not know that these numbers are not greater than 255, it won't be able to replace the plus operator with a bit-wise or.


Some refinements are possible:

We intend to interpret the arguments as non-negative; we should ideally state that in the code.

We also assume that the arguments contain a maximum of eight significant bits; we should state that assumption too in code.

There is no guarantee that an unsigned int can hold 32 bits; if we want to talk about portability in a pedantic way, we need to take care of that too.

Something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include  <type_traits>
#include <limits>
#include <cassert>

typename std::enable_if< ( std::numeric_limits<unsigned int>::digits > 31 ), unsigned int >::type
compile_int( unsigned int lso, unsigned int b, unsigned int c, unsigned int mso )
{
    assert( (lso<0xff) && (b<0xff) && (c<0xff) && (mso<0xff) ) ;
    // if( !( (lso<0xff) && (b<0xff) && (c<0xff) && (mso<0xff) ) ) 
    //      throw std::length_error( /* .... */ ) ; 

    return lso | ( b << 8U ) | ( c << 16U ) | ( mso << 24U ) ;
}


Last edited on
Disch wrote
bitshifting is way faster than looped division.

Yes, it is.

Disch wrote

it's also more clear.

I wouldn't say that if I were a beginner.

Duoas wrote
If you are going to give advice, don't tell newbies to ignore good advice for marginal, harder-to-understand advice.


Sorry, I'll pay more attention next time.
>> bitshifting is way faster than looped division.
> Yes, it is.

No, it isn't - not if the division is by a power of two, and the number of times the loop executes can be figured out at compile time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
inline unsigned int divide_in_loop( unsigned int n, unsigned int id )
{
    for( int i=0 ; i < id ; ++i ) n /= 256 ;
    return n ;
}

inline unsigned int bit_wise_shift( unsigned int n, unsigned int id )
{
  id *= 8; // 8 bits per byte
  return (n >> id) & 0xFF;
}

unsigned int using_divide_in_loop( unsigned int n )
{
    return divide_in_loop( n, 3 ) ;
}

unsigned int using_bit_wise_shift( unsigned int n )
{
    return bit_wise_shift( n, 3 ) ;
}


Compiled with g++ -O3 -fomit-frame-pointer generated:
1
2
3
4
5
6
7
8
9
__Z20using_divide_in_loopj:
	movl	4(%esp), %eax
	shrl	$24, %eax
	ret

__Z20using_bit_wise_shiftj:
	movl	4(%esp), %eax
	shrl	$24, %eax
	ret

Last edited on
mother of optimisation!!!!
Pages: 1234