Request for source code review of simple Ising model

U

Udyant Wig

| That merely asserts that:
|
| In particular, it's not uncommon for the rand funciton to produce
| alternately even and odd numbers, such that if you repeatedly
| compute rand % 2, you'll get the decidedly non-random sequence 0,
| 1, 0, 1, 0, 1, 0, 1... .
|
| I'm looking for concrete examples of real-world implementations that
| behave this way.

As Ike Naar writes in the article with
Message-ID: <[email protected]>:
| Here it prints
|
| 0101010101010101010101010101010101010101010101010101010101010101
|
| The implementation is gcc version 4.5.3 (NetBSD nb2 20110806).

This is the implementation of rand() in NetBSD's libc:

/* $NetBSD: rand.c,v 1.12 2012/06/25 22:32:45 abs Exp $ */

/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)rand.c 8.1 (Berkeley) 6/14/93";
#else
__RCSID("$NetBSD: rand.c,v 1.12 2012/06/25 22:32:45 abs Exp $");
#endif
#endif /* LIBC_SCCS and not lint */

#include <sys/types.h>
#include <stdlib.h>

static u_long next = 1;

int
rand(void)
{
/* LINTED integer overflow */
return (int)((next = next * 1103515245 + 12345) % ((u_long)RAND_MAX + 1));
}

void
srand(u_int seed)
{
next = seed;
}
 
U

Udyant Wig

| For instance, your code uses
|
| double random_value = (double) (rand () % 100) / 100.0;
|
| It is guaranteed that 0 <= random_value && random_value < 1.0, and
| (after allowing for floating point round-off), random_value will be an
| integer multiple of 0.01. However, it will not produce all 100
| different possible values with equal probability (not even if rand()
| were an ideal random number generator). See if you can figure out why.
| Hint: if RAND_MAX were 32799, it would work.

Qualitatively, if RAND_MAX is 32767, then rand () % 100 will produce
values between 0 and 67 with a higher probability than it will those
between 68 and 99.
 
U

Udyant Wig

|> The two outputs are equal. Independent confirmations by Emacs Lisp,
|>
|> (string-equal
|> "1011110011010110000010110001111000111010111101001010100100011101"
|> "1011110011010110000010110001111000111010111101001010100100011101")
|> => t
|>
|> and their MD5 sums,
|>
|> $ md5sum # Keith Thompson's output
|> 1011110011010110000010110001111000111010111101001010100100011101
|> 89e91cff9eefca6a24afaee2809c0859 -
|>
|> $ md5sum # Udyant Wig's output
|> 1011110011010110000010110001111000111010111101001010100100011101
|> 89e91cff9eefca6a24afaee2809c0859 -
|>
|> verify this.
|>
|
| Serious Q : are you taking the piss?

I went overboard with the confirmations. It was _not_ my intention to
abuse trust at all. I apologize if that came across as mocking.
 
G

glen herrmannsfeldt

(snip)
Qualitatively, if RAND_MAX is 32767, then rand () % 100 will produce
values between 0 and 67 with a higher probability than it will those
between 68 and 99.

And how many calls will it take for the difference to be
statistically significant?

-- glen
 
S

Stefan Ram

glen herrmannsfeldt said:
And how many calls will it take for the difference to be
statistically significant?

When you have used rand()%100 in a program or a library and
then release this to third parties, how do you know or
control how often this program is being started or the
library function is being called?
 
K

Keith Thompson

Ike Naar said:
Here it prints

0101010101010101010101010101010101010101010101010101010101010101

The implementation is gcc version 4.5.3 (NetBSD nb2 20110806).

Fascinating. So there *are* modern implementations that have this
problem.

The relevant implementation is the C standard library, not the compiler.
BSD has its own C standard library implementation. I wonder if Mac OSX
(which is based on BSD) has this problem.

The man page
http://netbsd.gw.com/cgi-bin/man-cgi?rand+3+NetBSD-current
doesn't mention this, apart from the NAME section:

rand, srand, rand_r -- bad random number generator
 
I

Ian Collins

Keith said:
Fascinating. So there *are* modern implementations that have this
problem.

The relevant implementation is the C standard library, not the compiler.
BSD has its own C standard library implementation. I wonder if Mac OSX
(which is based on BSD) has this problem.

It doesn't.

1110000011010011110011110110011001011111101011010111000000010010
The man page
http://netbsd.gw.com/cgi-bin/man-cgi?rand+3+NetBSD-current
doesn't mention this, apart from the NAME section:

rand, srand, rand_r -- bad random number generator

But it does have the same man page!
 
S

Stefan Ram

Ian Collins said:
But it does have the same man page!

IIRC, one version of the C standard contained a simplistic
example of how rand() might be implemented, and many
implementation manufacturers seemed to have copied this.

I am using rand() in my courses, because rand() is a simple
example of a function that can be called before function-call
arguments are introduced, and I therefore hope that rand()
will not be removed from the standard library of C.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3841.pdf
 
S

Stefan Ram

glen herrmannsfeldt said:
And how many calls will it take for the difference to be
statistically significant?

There might be other factors that hide those relatively
small differences.

This program shows the relative deviations in per thousand
when using »rand() %« in one single case:

#include <stdio.h> /* printf */
#include <stdlib.h> /* RAND_MAX, rand() */
#define N 5e6
#define COVER 10
int a[ COVER + 2 ];
int main( void )
{ for( int i = 0; i < N; ++i )++a[ 1 + rand() % COVER ];
for( int i = 0; i < COVER; ++i )
printf( "%d:%+f\n", i, 1000*( a[ i + 1 ]/( N / COVER )- 1 )); }

0:+0.396000
1:-1.370000
2:+0.070000
3:-0.572000
4:-0.154000
5:-1.236000
6:+0.004000
7:+4.234000
8:-0.470000
9:-0.902000

I.e., the number 7 is more frequent by a factor of about 1.004234
than in the case of equi partition.

Now, let's try this with the Mersenne Twister 19937 generator from C++:

#include <stdio.h> /* printf */
#include <stdlib.h> /* RAND_MAX, rand() */
#include <random> /* ::std::mt19937, ::std::uniform_int_distribution */
#define N 5e6
#define COVER 10
int a[ COVER + 2 ];
int main( void )
{ ::std::mt19937 mt19937;
::std::uniform_int_distribution< int >distribution{ 0, COVER - 1 };
auto const dice =[ & ]()-> int{ return distribution( mt19937 ); };
for( int i = 0; i < N; ++i )++a[ 1 + dice() ];
for( int i = 0; i < COVER; ++i )
printf( "%d:%+f\n", i, 1000*( a[ i + 1 ]/( N / COVER )- 1 )); }

0:-0.208000
1:+0.524000
2:-0.854000
3:+0.164000
4:-0.066000
5:-2.100000
6:+1.604000
7:+0.078000
8:+1.804000
9:-0.946000

So, the distribution is not so much worse when using »rand()%«
in the special case of my above test and the C implementation used.
 
K

Keith Thompson

IIRC, one version of the C standard contained a simplistic
example of how rand() might be implemented, and many
implementation manufacturers seemed to have copied this.

The 1990, 1999, and 2011 ISO C standards all include a non-normative
sample implementation of rand() (one that assume RAND_MAX==32767) -- but
it doesn't suffer from the problem that the low-order bits consistently
alternate between 0 and 1.
I am using rand() in my courses, because rand() is a simple
example of a function that can be called before function-call
arguments are introduced, and I therefore hope that rand()
will not be removed from the standard library of C.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3841.pdf

The link is to proposal to discourage the use of rand() in C++14. I
don't think there's much chance rand() will be removed from C any time
soon. If it were removed, there would have to be a better replacement
for it. There's no shortage of better PRNGs, but I expect that deciding
which one (if any) to mandate would be difficult.

I suppose that a future standard could add a new PRNG that can be seeded
with more information than the existing srand(), which only takes a
single unsigned int.

One requirement that rand() must satisfy is that it always yields the
same sequence of numbers for a given seed. That might not be compatible
with the requirements of cryptography, for example, unless you also
provide way to feed it a high-quality seed.
 
K

Keith Thompson

Keith Thompson said:
Fascinating. So there *are* modern implementations that have this
problem.

The relevant implementation is the C standard library, not the compiler.
BSD has its own C standard library implementation. I wonder if Mac OSX
(which is based on BSD) has this problem.

The man page
http://netbsd.gw.com/cgi-bin/man-cgi?rand+3+NetBSD-current
doesn't mention this, apart from the NAME section:

rand, srand, rand_r -- bad random number generator

I've grabbed a copy of source code for the NetBSD libc implementation of
rand() and run the same test on it on some of my own systems (64-bit
Linux Mint 14, 32-bit Ubuntu 12.10ish, and Cygwin on Windows 7), and I
*didn't* see alternating 0 and 1 low-order bits. I even examined all
available versions from the NetBSD CVS repository (the history goes back
to 1993), and I see no differences in the available history that would
explain this behavior. Obviously I'm missing something.

Ike: What is "NetBSD nb2 20110806"? The latest release of NetBSD is
6.1.4; how does it relate to that?

I've set up a GitHub project for this at
https://github.com/Keith-S-Thompson/crude_rand_test
 
U

Udyant Wig

(e-mail address removed)-berlin.de (Stefan Ram) writes:
| This program shows the relative deviations in per thousand
| when using »rand() %« in one single case:
|
| #include <stdio.h> /* printf */
| #include <stdlib.h> /* RAND_MAX, rand() */
| #define N 5e6
| #define COVER 10
| int a[ COVER + 2 ];
| int main( void )
| { for( int i = 0; i < N; ++i )++a[ 1 + rand() % COVER ];
| for( int i = 0; i < COVER; ++i )
| printf( "%d:%+f\n", i, 1000*( a[ i + 1 ]/( N / COVER )- 1 )); }
|
| 0:+0.396000
| 1:-1.370000
| 2:+0.070000
| 3:-0.572000
| 4:-0.154000
| 5:-1.236000
| 6:+0.004000
| 7:+4.234000
| 8:-0.470000
| 9:-0.902000

I get this:

0:-2.388000
1:-0.510000
2:-1.028000
3:-1.276000
4:+3.250000
5:-0.390000
6:-1.664000
7:+1.018000
8:+2.154000
9:+0.834000

There is a greater number of 4's and 8's.
 
I

Ike Naar

I've grabbed a copy of source code for the NetBSD libc implementation of
rand() and run the same test on it on some of my own systems (64-bit
Linux Mint 14, 32-bit Ubuntu 12.10ish, and Cygwin on Windows 7), and I
*didn't* see alternating 0 and 1 low-order bits. I even examined all
available versions from the NetBSD CVS repository (the history goes back
to 1993), and I see no differences in the available history that would
explain this behavior. Obviously I'm missing something.

Ike: What is "NetBSD nb2 20110806"? The latest release of NetBSD is
6.1.4; how does it relate to that?

$ uname -a
NetBSD iceland 6.1_STABLE NetBSD 6.1_STABLE (SDF6.amd64) #0: Tue Nov 26 12:49:14 UTC 2013 root@bjork:/spare/netbsd/src/sys/arch/amd64/compile/SDF6.amd64 amd64
 
J

James Kuyper

(snip)


And how many calls will it take for the difference to be
statistically significant?

A relatively tiny number, for any program involving serious number
crunching. It's a difference of 1/327, which is fairly large number for
certain purposes, and a completely negligible one for other purposes -
the key point is making sure you know which category your particular
case involves.
 
U

Udyant Wig

| James Kuyper <[email protected]| writes:
| | And that brings to mind another issue. Once you're sure that your
| | program is working, and you've reached the point where it's
| | reasonable to worry about performance, you might consider an
| | alternative data storage strategy. You're using one byte to store
| | one cell representing a spin 1/2 object: it therefore has only two
| | quantized orientations, up and down, so you're storing only one bit
| | of information for that cell. It might be better to use, for
| | example, a 32-bit integer to store the values of 32 consecutive
| | cells. Not only will this use up a lot less memory, but with (quite)
| | a bit of ingenuity, you can use bit-wise operations to handle all 32
| | cells at the same time, for a naive speed-up by a factor of 32. In
| | practice, the speed up will actually be less than that, but should
| | still be substantial. On the other hand, if you implement this idea
| | your code will be a lot more complicated, and much harder to
| | understand. You'll have to decide whether that's a acceptable cost
| | for the increased processing speed and decreased memory
| | requirements.
|
| This would be another rewrite of the program, as much of the existing
| source would have to be modified for this data representation.
|
| It should be worth looking into, because, as I said elsewhere, a
| given run of a 10x10 lattice can take upwards of three hours and not
| complete, while a 6x6 one finishes in mere minutes.

I found this to be somewhat difficult initially. After thinking and
writing, and some more thinking and rewriting, I developed the appended
source. Though I have not yet found out a way to handle 32 bits at
once, I did manage to use as many 32-bit integers as are required to
house dimension * dimension bits. So, a single int32_t can contain
upto 5x5 bits, while two can contain upto 8x8, etc.

Testing showed that this is faster than the earlier version wherein I
used the integers as mere sources of bits for the lattice.

The general structure remains the same.


/* common.h begins */

#ifndef COMMON_H
#define COMMON_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <stdbool.h>
#include <time.h>
#include <ctype.h>
#include <stdint.h>

#endif

/* common.h ends */


/* utilities.h begins */

#ifndef UTILITIES_H
#define UTILITIES_H

#include "common.h"

bool is_all_zeroes (char *string);
bool is_positive_integer (char *string);
int how_many (const char *s, int c);
char *copy_substring (char *substring, const char *string, size_t start, size_t end);
int minimum_multiple_32 (int n);

#endif

/* utilities.h ends */


/* utilities.c begins */

#include "utilities.h"

bool is_all_zeroes (char *string)
{
return string [strspn (string, "0")] == '\0';
}

bool is_positive_integer (char *string)
{
char *sp;

for (sp = string; *sp != '\0'; sp++) {
if (!isdigit (*sp)) {
return false;
}
}

return !is_all_zeroes (string);
}

/* From /C: A Reference Manual/, 5th ed., by Harbison and Steele
* page 352
*/
int how_many (const char *s, int c)
{
int n = 0;
if (c == 0) return 0;
while (s) {
s = strchr (s, c);
if (s) n++, s++;
}
return n;
}

char *copy_substring (char *substring, const char *string, size_t start, size_t end)
{
size_t length = end - start + 1;

errno = 0;
substring = malloc (1 + length);
if (substring == NULL) {
fprintf (stderr, "copy_substring: %s\n", strerror (errno));
exit (1);
}

memset (substring, 0, 1 + length);
strncpy (substring, string + start, length - 1);
substring [length] = '\0';

return substring;
}

int minimum_multiple_32 (int n)
{
int mm32 = n >> 5;
if (n % 32 != 0) {
mm32++;
}
return mm32;
}

/* utilities.c ends */


/* bitsing.h begins */

#ifndef BITSING_H
#define BITSING_H

/* 32-bit integer bit source that doubles as lattice */
int32_t *allocate_lattice (size_t size);
void initialize_lattice (int32_t *lattice);
void print_lattice (int32_t *lattice);

/* Energies vector */
int *allocate_energies (size_t size);
void initialize_energies (int *energies);

/* Core */
int check_and_add_neighbor (int32_t *lattice, int32_t cell, int col, int row);
void iterate (int32_t *lattice, int *energies);

/* Miscellaneous */
int count_ones (int32_t *lattice);
void print_iteration (int32_t *lattice, const char *format_string, int count);

/* Error checking */
void minority_error (void);
void beta_error (void);
void dimension_error (void);

#endif

/* bitsing.h ends */


/* bitsing.c begins */

#include "common.h"
#include "bitsing.h"
#include "utilities.h"

#define INTSRC_BITS 32

static int dimension = 3;
static double beta = 0.5;
static int minority_size = 1;

/* 32-bit integer bit source that doubles as lattice */
int32_t *allocate_lattice (size_t size)
{
int32_t *lattice;
lattice = malloc (size);
if (lattice == NULL) {
fprintf (stderr, "allocate_lattice: %s\n", strerror (errno));
exit (2);
}
return lattice;
}

void initialize_lattice (int32_t *lattice)
{
int int_count = minimum_multiple_32 (dimension * dimension);
int32_t *ip = lattice, *end = ip + int_count;
while (ip < end) {
*ip = (int32_t) (rand () % RAND_MAX);
ip++;
}
}

void print_lattice (int32_t *lattice)
{
int row, col;
int position;

for (col = 0; col < dimension; col++) {
for (row = 0; row < dimension; row++) {
position = col * dimension + row;
printf ("%d", (lattice [position / INTSRC_BITS] >> (position % INTSRC_BITS)) & 1);
}
putchar ('\n');
}
}


/* Energies vector */
int *allocate_energies (size_t size)
{
int *energies;
energies = malloc (size);
if (energies == NULL) {
fprintf (stderr, "allocate_energies: %s\n", strerror (errno));
exit (2);
}
return energies;
}

void initialize_energies (int *energies)
{
int *ep = energies, *end = ep + dimension * dimension;
while (ep < end) {
*ep = 0;
ep++;
}
}


/* Core */
int check_and_add_neighbor (int32_t *lattice, int cell_bit, int col, int row)
{
int neighbor_bit;
int position;
if (0 <= col && 0 <= row && col < dimension && row < dimension) {
position = col * dimension + row;
neighbor_bit = (lattice [position / INTSRC_BITS] >> (position % INTSRC_BITS)) & 1;
return cell_bit ^ neighbor_bit;
}
return 0;
}

void iterate (int32_t *lattice, int *energies)
{
int rrow, rcol;
int position;
int rcell_bit, ccell_bit;
int old_energy;
int new_energy;
int delta;

rrow = rand () % dimension;
rcol = rand () % dimension;
position = rcol * dimension + rrow;
rcell_bit = (lattice [position / INTSRC_BITS] >> (position % INTSRC_BITS)) & 1;
ccell_bit = rcell_bit ^ 1; /* comlemented cell */
old_energy = energies [position];

new_energy = check_and_add_neighbor (lattice, ccell_bit, rrow - 1, rcol);
new_energy = check_and_add_neighbor (lattice, ccell_bit, rrow + 1, rcol);
new_energy = check_and_add_neighbor (lattice, ccell_bit, rrow, rcol - 1);
new_energy = check_and_add_neighbor (lattice, ccell_bit, rrow, rcol + 1);

delta = new_energy - old_energy;
if (delta < 0) {
lattice [position / INTSRC_BITS] ^= (1 << (position % INTSRC_BITS));
energies [position] = new_energy;
}
else {
double ising_window = exp (-(beta * delta));
double random_value = (double) (rand () % 100) / 100.0;

if (random_value < ising_window) {
lattice [position / INTSRC_BITS] ^= (1 << (position % INTSRC_BITS));
energies [position] = new_energy;
}
}
}


/* Miscellaneous */
int count_ones (int32_t *lattice)
{
int count = 0;
int int_count = minimum_multiple_32 (dimension * dimension);
int32_t *ip = lattice, *end = ip + int_count;
int field;
int i = 0;

while (ip < end) {
for (field = *ip; field; field >>= 1) {
if (i == dimension * dimension) {
return count;
}
count += field & 1;
i++;
}
ip++;
}

return count;
}

static const char initial_format [] = \
"Initial configuration %6d\n----------------------------\n";
static const char iteration_format [] = \
"Iteration %6d\n----------------\n";
static const char final_format [] = \
"Final configuration %6d\n--------------------------\n";

void print_iteration (int32_t *lattice, const char *format_string, int count)
{
putchar ('\n');
printf (format_string, count);
print_lattice (lattice);
}


/* Error checking */
void minority_error (void)
{
exit (2);
}

void beta_error (void)
{
exit (2);
}

void dimension_error (void)
{
exit (2);
}


/* Return codes:
* 0 -- success
* 1 -- memory allocation failure
* 2 -- invalid command line argument
*/
int main (int argc, char *argv [])
{
if (argc > 3) {
if (is_positive_integer (argv [3]) || is_all_zeroes (argv [3])) {
minority_size = atoi (argv [3]);
}
else {
minority_error ();
}

if (minority_size < 0) {
minority_size = 0;
}
else if (minority_size > dimension * dimension) {
minority_size = dimension * dimension;
}
else if (minority_size > (dimension * dimension) / 2) {
minority_size = (dimension * dimension) - minority_size;
}
}
if (argc > 2) {
char *beta_string = argv [2];
size_t length = strlen (beta_string);

if (how_many (beta_string, '.') == 1) {
size_t position_decimal = strcspn (beta_string, ".");
char *integral_part = NULL;
char *fractional_part = NULL;

if (position_decimal == 0) {
beta_error ();
}
if (position_decimal == strlen (beta_string) - 1) {
beta_error ();
}

integral_part = copy_substring (integral_part, \
beta_string, \
0, \
position_decimal - 1);
if (is_positive_integer (integral_part) || \
is_all_zeroes (integral_part)) {
fractional_part = copy_substring (fractional_part, \
beta_string, \
position_decimal + 1, \
length - 1);
if (is_positive_integer (fractional_part) || \
is_all_zeroes (fractional_part)) {
beta = atof (argv [2]);
}
else {
beta_error ();
}

free (fractional_part);
}
else {
beta_error ();
}

free (integral_part);
}
else {
beta_error ();
}
}
if (argc > 1) {
if (is_positive_integer (argv [1]))
dimension = atoi (argv [1]);
else {
dimension_error ();
}
}

srand (time (0));

int32_t *lattice;
lattice = allocate_lattice (minimum_multiple_32 (dimension * dimension) \
* sizeof *lattice);
initialize_lattice (lattice);

int *energies;
energies = allocate_energies (dimension * dimension * sizeof *energies);
initialize_energies (energies);

print_iteration (lattice, initial_format, 0);

long count = 1;
while (true) {
iterate (lattice, energies);

int ones_count = count_ones (lattice);
if ((ones_count == minority_size) || \
((dimension * dimension - ones_count) == minority_size)) {
break;
}

print_iteration (lattice, iteration_format, count++);
}

print_iteration (lattice, final_format, count);
free (energies);
free (lattice);

exit (0);
}

/* bitsing.c ends */
 
K

Keith Thompson

Udyant Wig said:
bool is_positive_integer (char *string)
{
char *sp;

for (sp = string; *sp != '\0'; sp++) {
if (!isdigit (*sp)) {
return false;
}
}

return !is_all_zeroes (string);
}

I see you're still not casting the argument to isdigit(). That causes
undefined behavior if plain char is signed and your string contains a
character with a negative value (which probably represents a character
greater than 127).

[...]
 
I

Ike Naar

Can you try running the code from my new GitHub project on your system?

https://github.com/Keith-S-Thompson/crude_rand_test

Testing the low-order bits of the result of rand() from current implementation
0101010101010101010101010101010101010101010101010101010101010101
Low order bits alternate 0 and 1 (bad)

Testing the low-order bits of the result of rand() from ISO C sample implementation
0101010101010101010101010101010101010101010101010101010101010101
Low order bits alternate 0 and 1 (bad)

Testing the low-order bits of the result of rand() from NetBSD
0101010101010101010101010101010101010101010101010101010101010101
Low order bits alternate 0 and 1 (bad)
 
J

jacob navia

Le 19/04/2014 23:39, Keith Thompson a écrit :
I wonder if Mac OSX
(which is based on BSD) has this problem.

No, not at first sight anyway No repeating pattern as far as I see:

1110000011010011110011110110011001011111101011010111000000010010

My mac is like this:

uname -a
Darwin macpro.local 13.1.0 Darwin Kernel Version 13.1.0: Thu Jan 16
19:40:37 PST 2014; root:xnu-2422.90.20~2/RELEASE_X86_64 x86_64

It is a nice machine, the best Unix I have ever used.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,073
Messages
2,570,539
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top