I'm trying to pull an integer worth of data from /dev/random. The code below works. What I'm trying to understand is why I have to use the size_t type for the final output in order to avoid loss of precision. See the comments in the code.
I thought that perhaps the problem was the .read method adding some type of padding such as a null terminating character, I tried setting the length to 3 to avoid this but it didn't seem to matter. I'm glad a figured out how to resolve this, but I would like to understand why I had to.
size_t getSeed()
{
std::ifstream rnd ("/dev/random", std::ios::binary);
if( rnd.is_open() )
{
int len = sizeof(int); // 4 bytes
char* blk = new char [len];
rnd.read ( blk, len ); // 8 bytes?
rnd.close();
size_t out = (size_t)*blk; // loss of precision with int
delete[] blk;
return out;
}
else
{
return 0;
}
}
I cannot undrstand why you read 4 char
in binary mode and then save them to int
, i.e. to size_t
. It would be easier to read from stream to size_t out
directly:
size_t out;
rnd.read ( &out, sizeof(out) );
But, if it just an experiment I want to propose you some variants to pack 4 char into one 32-bit int.
There is the first (C style) option with union
:
#include <cstdint>
union ConversionUnion
{
int32_t dint;
char dchar[4];
};
int32_t getSeed()
{
std::ifstream rnds ("/dev/random", std::ios::binary);
ConversionUnion conv;
if( rnds.is_open() )
{
rnds.read (conv.dchar, sizeof(int32_t ));
rnds.close();
return conv.dint;
}
else
{
return 0;
}
}
And if you just want to fix your code, try change line
size_t out = (size_t)*blk;
to line (this is also rather C than C++)
size_t out = *((size_t*)blk);
Also consider solution that was for 4 numbers (not array) - here. But the same approach can be used for array:
int32_t result = 0;
for(int i = 0; i < 4; i++) // or
// for(int i = 3; i > 0; i--) // for reverse order of bytes if needed
{
result <<= 8;
result |= blk[i];
}