-3

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;
    }
}  
mreff555
  • 1,049
  • 1
  • 11
  • 21
  • @mreff555 What did you just do? – LogicStuff Jul 16 '16 at 13:48
  • LogicStuff, what do you mean? Paul R, what would be the correct way to do this conversion? – mreff555 Jul 16 '16 at 13:54
  • 1
    Even if it was right (you're just casting the first `char`), why would you edit it in? – LogicStuff Jul 16 '16 at 13:57
  • 1
    This is the only way I know how to do it. I'm asking for a better method. Everyone seems to know how not to do it, but what I'm actually looking for is a solution. At this point I have been told casting is bad more than once. More criticism is not constructive and just rude. If you don't have a proper solution, please just move on and allow someone capable of answering the question to do so. – mreff555 Jul 16 '16 at 14:08
  • 1
    You've disrupted your post by adding that `*`, not removing `// loss of precision with int`, because it does not happen now. And *"The code below works"* - was this ever true? It just compiled, it never "worked". `int out = (int &) *blk;` is one example of making it work, but depends on endianness. – LogicStuff Jul 16 '16 at 14:14
  • My original code had the *, but I was getting frustrated and trying lots of different tests to get it right. I accidentally left out the * when I originally posted the message. However no, this did not fix the loss of precision issue. – mreff555 Jul 16 '16 at 15:49

1 Answers1

1

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];
    }
Community
  • 1
  • 1
VolAnd
  • 6,367
  • 3
  • 25
  • 43
  • Perfect. Union sounds like what I was looking for. To answer your first concern the reason I only read 4 bytes was because I would only trying to output 4 bytes. However, the problem was that the blk variable seems to swell to 8 bytes. – mreff555 Jul 16 '16 at 14:11
  • For 64-bit and 8 chars change union to `union ConversionUnion { int64_t dint; char dchar[8]; }; ` and use `sizeof(int64_t )` as the second argument of `read` – VolAnd Jul 16 '16 at 14:31
  • 1
    Better `union ConversionUnion { int64_t dint; char dchar[sizeof(dint)]; };` and `rnds.read(conv.dchar, sizeof(conv.dchar));` – LogicStuff Jul 16 '16 at 14:47