Search code examples
c++fstream

loss of precision from 4 byte char array to int?


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;
    }
}  

Solution

  • 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];
        }