Use explicit 32-bit integers in random number system functions (issue #661).

This simplifies the code by making it independent of the size of 'long', and
fixes the behaviour of urandom_range when the upper limit is > 0x7fffffff.
This commit is contained in:
Martin Whitaker 2022-04-03 19:45:44 +01:00
parent d480c4d7d0
commit 94e09ec473
1 changed files with 82 additions and 100 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2021 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2022 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -25,34 +25,35 @@
# include <math.h> # include <math.h>
# include <limits.h> # include <limits.h>
#if ULONG_MAX > 4294967295UL /*
# define UNIFORM_MAX INT_MAX * The following code is largely copied from the IEEE standard (section 17.9.3
# define UNIFORM_MIN INT_MIN * in IEEE 1364-2005, Annex N in IEEE 1800-2017). The code in the IEEE standard
#else * predates the widespread availability of 64-bit processors, so assumes 'long'
# define UNIFORM_MAX LONG_MAX * is a 32 bit value (https://accellera.mantishub.io/view.php?id=1136). So we
# define UNIFORM_MIN LONG_MIN * replace 'long' with 'int32_t' to ensure correct and consistent behaviour
#endif * regardless of how the code is built.
*/
static double uniform(long *seed, long start, long end); static double uniform(int32_t *seed, int32_t start, int32_t end);
static double normal(long *seed, long mean, long deviation); static double normal(int32_t *seed, int32_t mean, int32_t deviation);
static double exponential(long *seed, long mean); static double exponential(int32_t *seed, int32_t mean);
static long poisson(long *seed, long mean); static int32_t poisson(int32_t *seed, int32_t mean);
static double chi_square(long *seed, long deg_of_free); static double chi_square(int32_t *seed, int32_t deg_of_free);
static double t(long *seed, long deg_of_free); static double t(int32_t *seed, int32_t deg_of_free);
static double erlangian(long *seed, long k, long mean); static double erlangian(int32_t *seed, int32_t k, int32_t mean);
static long rtl_dist_chi_square(long *seed, long df) static int32_t rtl_dist_chi_square(int32_t *seed, int32_t df)
{ {
double r; double r;
long i; int32_t i;
if (df > 0) { if (df > 0) {
r = chi_square(seed, df); r = chi_square(seed, df);
if (r >= 0) { if (r >= 0) {
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
} else { } else {
r = -r; r = -r;
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
i = -i; i = -i;
} }
} else { } else {
@ -64,18 +65,18 @@ static long rtl_dist_chi_square(long *seed, long df)
return i; return i;
} }
static long rtl_dist_erlang(long *seed, long k, long mean) static int32_t rtl_dist_erlang(int32_t *seed, int32_t k, int32_t mean)
{ {
double r; double r;
long i; int32_t i;
if (k > 0) { if (k > 0) {
r = erlangian(seed, k, mean); r = erlangian(seed, k, mean);
if (r >= 0) { if (r >= 0) {
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
} else { } else {
r = -r; r = -r;
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
i = -i; i = -i;
} }
} else { } else {
@ -87,18 +88,18 @@ static long rtl_dist_erlang(long *seed, long k, long mean)
return i; return i;
} }
static long rtl_dist_exponential(long *seed, long mean) static int32_t rtl_dist_exponential(int32_t *seed, int32_t mean)
{ {
double r; double r;
long i; int32_t i;
if (mean > 0) { if (mean > 0) {
r = exponential(seed, mean); r = exponential(seed, mean);
if (r >= 0) { if (r >= 0) {
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
} else { } else {
r = -r; r = -r;
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
i = -i; i = -i;
} }
} else { } else {
@ -110,26 +111,26 @@ static long rtl_dist_exponential(long *seed, long mean)
return i; return i;
} }
static long rtl_dist_normal(long *seed, long mean, long sd) static int32_t rtl_dist_normal(int32_t *seed, int32_t mean, int32_t sd)
{ {
double r; double r;
long i; int32_t i;
r = normal(seed, mean, sd); r = normal(seed, mean, sd);
if (r >= 0) { if (r >= 0) {
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
} else { } else {
r = -r; r = -r;
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
i = -i; i = -i;
} }
return i; return i;
} }
static long rtl_dist_poisson(long *seed, long mean) static int32_t rtl_dist_poisson(int32_t *seed, int32_t mean)
{ {
long i; int32_t i;
if (mean > 0) { if (mean > 0) {
i = poisson(seed, mean); i = poisson(seed, mean);
@ -142,18 +143,18 @@ static long rtl_dist_poisson(long *seed, long mean)
return i; return i;
} }
static long rtl_dist_t(long *seed, long df) static int32_t rtl_dist_t(int32_t *seed, int32_t df)
{ {
double r; double r;
long i; int32_t i;
if (df > 0) { if (df > 0) {
r = t(seed, df); r = t(seed, df);
if (r >= 0) { if (r >= 0) {
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
} else { } else {
r = -r; r = -r;
i = (long) (r + 0.5); i = (int32_t) (r + 0.5);
i = -i; i = -i;
} }
} else { } else {
@ -165,36 +166,30 @@ static long rtl_dist_t(long *seed, long df)
return i; return i;
} }
/* copied from IEEE1364-2001, with slight modifications for 64bit machines. */ static int32_t rtl_dist_uniform(int32_t *seed, int32_t start, int32_t end)
static long rtl_dist_uniform(long *seed, long start, long end)
{ {
double r; double r;
long i; int32_t i;
if (start >= end) return(start); if (start >= end) return start;
/* NOTE: The cast of r to i can overflow and generate strange if (end != INT32_MAX) {
values, so cast to unsigned long first. This eliminates
the underflow and gets the twos complement value. That in
turn can be cast to the long value that is expected. */
if (end != UNIFORM_MAX) {
end++; end++;
r = uniform(seed, start, end); r = uniform(seed, start, end);
if (r >= 0) { if (r >= 0) {
i = (unsigned long) r; i = (int32_t) r;
} else { } else {
i = - ( (unsigned long) (-(r - 1)) ); i = (int32_t) (r - 1);
} }
if (i < start) i = start; if (i < start) i = start;
if (i >= end) i = end - 1; if (i >= end) i = end - 1;
} else if (start != UNIFORM_MIN) { } else if (start != INT32_MIN) {
start--; start--;
r = uniform( seed, start, end) + 1.0; r = uniform(seed, start, end) + 1.0;
if (r >= 0) { if (r >= 0) {
i = (unsigned long) r; i = (int32_t) r;
} else { } else {
i = - ( (unsigned long) (-(r - 1)) ); i = (int32_t) (r - 1);
} }
if (i <= start) i = start + 1; if (i <= start) i = start + 1;
if (i > end) i = end; if (i > end) i = end;
@ -203,25 +198,20 @@ static long rtl_dist_uniform(long *seed, long start, long end)
r = r * 4294967296.0 - 2147483648.0; r = r * 4294967296.0 - 2147483648.0;
if (r >= 0) { if (r >= 0) {
i = (unsigned long) r; i = (int32_t) r;
} else { } else {
/* At least some compilers will notice that (r-1) i = (int32_t) (r - 1);
is <0 when castling to unsigned long and
replace the result with a zero. This causes
much wrongness, so do the casting to the
positive version and invert it back. */
i = - ( (unsigned long) (-(r - 1)) );
} }
} }
return i; return i;
} }
static double uniform(long *seed, long start, long end ) static double uniform(int32_t *seed, int32_t start, int32_t end)
{ {
double d = 0.00000011920928955078125; double d = 0.00000011920928955078125;
double a, b, c; double a, b, c;
unsigned long oldseed, newseed; uint32_t oldseed, newseed;
oldseed = *seed; oldseed = *seed;
if (oldseed == 0) if (oldseed == 0)
@ -243,15 +233,8 @@ static double uniform(long *seed, long start, long end )
*/ */
newseed = 69069 * oldseed + 1; newseed = 69069 * oldseed + 1;
/* Emulate a 32-bit unsigned long, even if the native machine
* uses wider words.
*/
#if ULONG_MAX > 4294967295UL
newseed = newseed & 4294967295UL;
#endif
*seed = newseed; *seed = newseed;
#if 0 #if 0
/* Cadence-donated conversion from unsigned int to double */ /* Cadence-donated conversion from unsigned int to double */
{ {
@ -265,14 +248,13 @@ static double uniform(long *seed, long start, long end )
c = 1.0 + (newseed >> 9) * 0.00000011920928955078125; c = 1.0 + (newseed >> 9) * 0.00000011920928955078125;
#endif #endif
c = c + (c*d); c = c + (c*d);
c = ((b - a) * (c - 1.0)) + a; c = ((b - a) * (c - 1.0)) + a;
return c; return c;
} }
static double normal(long *seed, long mean, long deviation) static double normal(int32_t *seed, int32_t mean, int32_t deviation)
{ {
double v1, v2, s; double v1, v2, s;
@ -289,7 +271,7 @@ static double normal(long *seed, long mean, long deviation)
return s * v1 + v2; return s * v1 + v2;
} }
static double exponential(long *seed, long mean) static double exponential(int32_t *seed, int32_t mean)
{ {
double n; double n;
@ -301,9 +283,9 @@ static double exponential(long *seed, long mean)
return n; return n;
} }
static long poisson(long *seed, long mean) static int32_t poisson(int32_t *seed, int32_t mean)
{ {
long n; int32_t n;
double p, q; double p, q;
n = 0; n = 0;
@ -318,10 +300,10 @@ static long poisson(long *seed, long mean)
return n; return n;
} }
static double chi_square(long *seed, long deg_of_free) static double chi_square(int32_t *seed, int32_t deg_of_free)
{ {
double x; double x;
long k; int32_t k;
if (deg_of_free % 2) { if (deg_of_free % 2) {
x = normal(seed, 0, 1); x = normal(seed, 0, 1);
@ -336,7 +318,7 @@ static double chi_square(long *seed, long deg_of_free)
return x; return x;
} }
static double t( long *seed, long deg_of_free) static double t( int32_t *seed, int32_t deg_of_free)
{ {
double x, chi2, dv, root; double x, chi2, dv, root;
@ -348,10 +330,10 @@ static double t( long *seed, long deg_of_free)
return x; return x;
} }
static double erlangian(long *seed, long k, long mean) static double erlangian(int32_t *seed, int32_t k, int32_t mean)
{ {
double x, a, b; double x, a, b;
long i; int32_t i;
x = 1.0; x = 1.0;
for (i = 1; i <= k; i++) { for (i = 1; i <= k; i++) {
@ -533,8 +515,8 @@ static PLI_INT32 sys_random_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed = 0; vpiHandle callh, argv, seed = 0;
s_vpi_value val; s_vpi_value val;
static long i_seed = 0; static int32_t i_seed = 0;
long a_seed; int32_t a_seed;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -599,16 +581,16 @@ static PLI_INT32 sys_urandom_range_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
} }
/* From SystemVerilog. */ /* From SystemVerilog. */
static unsigned long urandom(long *seed, unsigned long max, unsigned long min) static uint32_t urandom(int32_t *seed, uint32_t max, uint32_t min)
{ {
static long i_seed = 0; static int32_t i_seed = 0;
unsigned long result; int32_t max_i, min_i;
long max_i, min_i; uint32_t result;
max_i = max + INT_MIN; max_i = max + INT32_MIN;
min_i = min + INT_MIN; min_i = min + INT32_MIN;
if (seed != 0) i_seed = *seed; if (seed != 0) i_seed = *seed;
result = rtl_dist_uniform(&i_seed, min_i, max_i) - INT_MIN; result = (uint32_t)rtl_dist_uniform(&i_seed, min_i, max_i) - INT32_MIN;
if (seed != 0) *seed = i_seed; if (seed != 0) *seed = i_seed;
return result; return result;
} }
@ -618,7 +600,7 @@ static PLI_INT32 sys_urandom_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed = 0; vpiHandle callh, argv, seed = 0;
s_vpi_value val; s_vpi_value val;
long i_seed; int32_t i_seed;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -636,9 +618,9 @@ static PLI_INT32 sys_urandom_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
/* Calculate and return the result. */ /* Calculate and return the result. */
if (seed) { if (seed) {
val.value.integer = urandom(&i_seed, UINT_MAX, 0); val.value.integer = urandom(&i_seed, UINT32_MAX, 0);
} else { } else {
val.value.integer = urandom(0, UINT_MAX, 0); val.value.integer = urandom(0, UINT32_MAX, 0);
} }
vpi_put_value(callh, &val, 0, vpiNoDelay); vpi_put_value(callh, &val, 0, vpiNoDelay);
@ -656,7 +638,7 @@ static PLI_INT32 sys_urandom_range_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, maxval, minval; vpiHandle callh, argv, maxval, minval;
s_vpi_value val; s_vpi_value val;
unsigned long i_maxval, i_minval; uint32_t i_maxval, i_minval;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -681,7 +663,7 @@ static PLI_INT32 sys_urandom_range_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
/* Swap the two arguments if they are out of order. */ /* Swap the two arguments if they are out of order. */
if (i_minval > i_maxval) { if (i_minval > i_maxval) {
unsigned long tmp = i_minval; uint32_t tmp = i_minval;
i_minval = i_maxval; i_minval = i_maxval;
i_maxval = tmp; i_maxval = tmp;
} }
@ -696,7 +678,7 @@ static PLI_INT32 sys_dist_uniform_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, start, end; vpiHandle callh, argv, seed, start, end;
s_vpi_value val; s_vpi_value val;
long i_seed, i_start, i_end; int32_t i_seed, i_start, i_end;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -733,7 +715,7 @@ static PLI_INT32 sys_dist_normal_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, mean, sd; vpiHandle callh, argv, seed, mean, sd;
s_vpi_value val; s_vpi_value val;
long i_seed, i_mean, i_sd; int32_t i_seed, i_mean, i_sd;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -770,7 +752,7 @@ static PLI_INT32 sys_dist_exponential_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, mean; vpiHandle callh, argv, seed, mean;
s_vpi_value val; s_vpi_value val;
long i_seed, i_mean; int32_t i_seed, i_mean;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -803,7 +785,7 @@ static PLI_INT32 sys_dist_poisson_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, mean; vpiHandle callh, argv, seed, mean;
s_vpi_value val; s_vpi_value val;
long i_seed, i_mean; int32_t i_seed, i_mean;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -836,7 +818,7 @@ static PLI_INT32 sys_dist_chi_square_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, df; vpiHandle callh, argv, seed, df;
s_vpi_value val; s_vpi_value val;
long i_seed, i_df; int32_t i_seed, i_df;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -869,7 +851,7 @@ static PLI_INT32 sys_dist_t_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, df; vpiHandle callh, argv, seed, df;
s_vpi_value val; s_vpi_value val;
long i_seed, i_df; int32_t i_seed, i_df;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */
@ -902,7 +884,7 @@ static PLI_INT32 sys_dist_erlang_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{ {
vpiHandle callh, argv, seed, k, mean; vpiHandle callh, argv, seed, k, mean;
s_vpi_value val; s_vpi_value val;
long i_seed, i_k, i_mean; int32_t i_seed, i_k, i_mean;
(void)name; /* Parameter is not used. */ (void)name; /* Parameter is not used. */