Read float and long from external EEPROM chip
1510543923 is stored as: 01011010 00001001 00010010 00110011


This is known as “big endian”, or “MSB first”, because the most
significant byte (MSB, here 01011010) comes first.

I recommend against this order. If you can, swap all the bytes and store
the value LSB first, i.e. with the least significant byte first, and the
most significant byte last. This will make your life easier since it's
the order used internally in the Arduino. If you can go little endian,
then you will be able to read the data straight into the required
variable, with no conversion needed, as in:

i2c_eeprom_read_buffer(0x50, 25, (byte *) &amp;j, sizeof j);


and it will work identically with any data type.

If you cannot choose the byte order (maybe you cannot control what is
inside the EEPROM), then you will have to reverse all the bytes. You can
do this either with bit shifts or by explicitly moving bytes.


  bytes[1] &lt;&lt; 16


This will not work. Per the rules of the C++ language, bytes[1] is
implicitly promoted to int type before the shift, then it is shifted
as an int. But on your Arduino an int is 16-bits long, thus by
shifting it 16 positions you are dropping off all of the bits.
Actually, this is even worse: you are invoking what is known as
undefined behavior, meaning that it is considered nonsense and the
compiler is free to interpret however it wants.

To do this properly, you first have to explicitly cast bytes[i] to
unsigned long. It has to be unsigned because changing the sign bit on
a long by bit shifting is also undefined behavior. Thus, the proper
way of reconstructing the number via bit shifts is:

long j = ((unsigned long) bytes[3] &lt;&lt; 0)
       | ((unsigned long) bytes[2] &lt;&lt; 8)
       | ((unsigned long) bytes[1] &lt;&lt; 16)
       | ((unsigned long) bytes[0] &lt;&lt; 24);


This is portable (it should also work on big-endian architectures) but
only works with integral types, not with floats.

For reconstructing a float, my preferred option would be to use a
union, in order to access the bytes of the variable explicitly. Then
you just have to copy the bytes from the buffer to the union in
reverse order:

union { float f; byte b[4]; } data;
data.b[0] = bytes[3];
data.b[1] = bytes[2];
data.b[2] = bytes[1];
data.b[3] = bytes[0];
float x = data.f;


One nice thing about this approach is that it works with any data type.