How can I generate random single precision (float32) numbers ?

조회 수: 28 (최근 30일)
Firan Lucian
Firan Lucian 2019년 7월 24일
댓글: Steven Lord 2019년 8월 12일
A SP float may be generated in a range from -Infinity to +Infinity or including NANs.
Full rage would contain all representable numbers 0x00000000 to 0xffffffff.
like typecast(uint32(hex2dec('00000000')), 'single') to typecast(uint32(hex2dec('ffffffff')), 'single')
What is the best approach ?
How about generate without NAN, or INF, or Denormalized numbers (subnormals) ?
Tried:
size_x = 1000;
size_y = 1;
% realmin('single') 1.1755e-38
% realmax('single') 3.4028e+38
a = realmin('single') + ( realmax('single') - realmin('single') ).*rand(size_x, size_y, 'single');
a_range = [min(a) mean(a) std(a) max(a)]
b = typecast(randi([0, intmax('uint32')], size_x, size_y, 'uint32'), 'single');
b_range = [min(b) mean(b) std(b) max(b)]
  댓글 수: 9
Guillaume
Guillaume 2019년 7월 26일
편집: Guillaume 2019년 7월 27일
Well, yes I did say that negative 0 appear sometimes. But it's undocumented, so maybe it won't always be the case (although I doubt it'll change) and there's no guarantee that some functions won't preserve it, as is the case with nan as you've pointed out.
And that's my point, the whole thing is undocumented on purpose. As a user of a high-level language you shouldn't care about these details. If you do, maybe matlab is not the language you should be using.

댓글을 달려면 로그인하십시오.

답변 (5개)

Stephen23
Stephen23 2019년 7월 24일
편집: Stephen23 2019년 7월 24일
This generates all possible singles:
>> V = ['0':'9','A':'F'];
>> X = randi(16,1,8);
>> V(X)
ans = 7B3A5B5F
>> N = typecast(uint32(hex2dec(V(X))),'single')
N = 9.6762e+035
and back again:
>> num2hex(N)
ans = 7b3a5b5f
"How about generate without NAN, or INF, or Denormalized numbers (subnormals) ?"
Read the specification for single and make sure that you do not generate those random numbers (you will probably need several randi calls, each with a different range, or to use setdiff or something similar).
See also:
  댓글 수: 5
Stephen23
Stephen23 2019년 7월 25일
편집: Stephen23 2019년 7월 25일
"hex2num() to do the conversion from char to numeric."
@Walter Roberson: can you please give an example of using hex2num, which (currently) only generates double class numerics, to generate a single class numeric (as the question requests)?
I based my answer on the code given here:
where MathWorks Support Team wrote: "Since the HEX2NUM only supports DOUBLE precision data, the following is a workaround for using only built-in functions to allow HEX2NUM type functionality to extent to SINGLE data". As far as I can tell, there has been no change to hex2num since that answer was written.

댓글을 달려면 로그인하십시오.


James Tursa
James Tursa 2019년 7월 24일
편집: James Tursa 2019년 7월 26일
For the generic answer with all bit patterns possible and selected with equal probability (including inf & nan & denorm) your "b" method is direct and straightforward and is the one I would use. Note that while there is only one bit pattern for -inf and one bit pattern for +inf, there are many bit patterns for nan values (any bit pattern with all 1-bits exponent and any non-zero bits mantissa will be a nan value). So it is very much more likely to get a nan with this method that it is to get a -inf or +inf. Also note that this method will generate +0 and -0 as two distinct bit patterns.
For the answer not including the special bit patterns, this can get tricky. Can we assume that you want all bit patterns except the special ones selected uniformly? If so, one could oversample and then downsize using isfinite( ) function and isdenorm( ) function. E.g., a brute force isdenorm( ) function could be something like this for single precison:
% Function returns true (element-wise) if element is denormalized number.
% s must be single precision float
% 2^(-126) is the smallest normalized single float number, realmin('single')
function g = isdenorm(s)
g = ( abs(s) < single(2^(-126)) ) & ( s ~= 0 );
end
Since the special bit patterns have exponent bits all 1's (inf and nan) or all 0's (denorm), you would simply have to oversample by about 1% or so on average (two of the 2^8 number of possible exponent bit patterns are mostly unwanted).
CAUTION: The above just shows the algorithm. A well written function would also include input argument checks which I haven't done.
*** EDIT ***
Here is some generation code based on Walter's suggestion:
% Generates random numbers for entire range of normalized single floats
% (Based on an idea by Walter Roberson)
% Note: Numbers are distributed uniformly across all possible bit patterns,
% they are NOT distributed uniformly across the range since floating
% point values are not uniformly distributed accross their range.
% dims = A vector containing the dimensions of the result
% Programmer: James Tursa
function r = rand_single_range(dims)
neg_sign_bit = typecast(-single(0),'uint32'); % Only the sign bit is set
smin = realmin('single'); % Smallest normalized single float
smax = realmax('single'); % Largest normalized single float
imin = typecast(smin,'uint32'); % uint32 containing bit pattern of smin
imax = typecast(smax,'uint32'); % uint32 containing bit pattern of smax
ishift = imax - imin + 1; % The amount to shift the neg values for sampling
kmax = imax + ishift + 1; % Encompass the pos + neg ranges + an extra 1 for zero
k = randi([imin kmax],prod(dims),1,'uint32'); % Generate the bit patterns
k(k==kmax) = 0; % Map the 0's first
kneg = k > imax; % Logical indexes of the (eventual) negative values
k(kneg) = k(kneg) + (neg_sign_bit - ishift); % Shift the negative values back in range and apply sign bit
r = reshape(typecast(k,'single'),dims); % Reshape result to desired dimensions
end
It generates about twice the range of bit patterns needed and then maps about half of them back into "negative" bit patterns. It does allow for a 0 bit pattern although it would be quite rare to actually get it in practice.
  댓글 수: 6
Guillaume
Guillaume 2019년 8월 5일
Not sure what the intent of that is
full_size = intmax('uint32') - intmin('uint32') + 1;
but it's the same as:
full_size = intmax('uint32') %ie full_size = 2^32-1
Note that intmin('uint32') is 0, intmax returns a uint32, and adding 1 to that overflows uint32 so the result is capped at intmax.
Maybe
full_size = 2^32;
would be simpler (if that was the intent).
Firan Lucian
Firan Lucian 2019년 8월 6일
Thanks Guillaume, updated code.
Having clear domain makes easy to write range checkers:
function [out] = isSubnormal(val)
% check if value is subnormal or denormalized SP number
%+subnormals range hex [0x00000001 0x007fffff]
%-subnormals range hex [0x80000001 0x807fffff]
int_v = typecast(single(val), 'uint32' );
pos_range = and (( int_v >= hex2dec('00000001') ) , ( int_v <= hex2dec('007fffff') ));
neg_range = and (( int_v >= hex2dec('80000001') ) , ( int_v <= hex2dec('807fffff') ));
out = or (pos_range , neg_range);
end

댓글을 달려면 로그인하십시오.


the cyclist
the cyclist 2019년 7월 24일
편집: the cyclist 2019년 7월 24일
According to the documentation here,
x = rand(10,1,"single")
will do it.
Your question has sufficient nuance that there could be some discrepancies "at the edges" that you might want to investigate further. But the above is almost certainly adequate for the vast majority of purposes.
  댓글 수: 3
Firan Lucian
Firan Lucian 2019년 7월 24일
Added c variant rand(size_x, size_y, "single") on large size elements, but range seems smaller. Best variant yet seems b.
size_x = 100000000;
size_y = 1;
a = realmin('single') + ( realmax('single') - realmin('single') ).*rand(size_x, size_y, 'single');
disp( [ sprintf("Var a Min: %e, Max: %e, Mean: %e, Std: %e", min(a), max(a), mean(a), std(a) ) ] );
t1a = kstest(a);
t2a = adtest(a);
t3a = jbtest(a);
disp( [ sprintf(" ktest: %d, adtest: %d, jbtest: %d ", t1a, t2a, t3a ) ] );
% remove nan and inf
a = a( ~isnan( a ) );
a = a( ~isinf( a ) );
a = double(a);
disp( [ sprintf(" Mean: %e, Std: %e ", mean( a ), std( a ) ) ] );
b = typecast(randi([0, intmax('uint32')], size_x, size_y, 'uint32'), 'single');
disp( [ sprintf("Var b Min: %e, Max: %e, Mean: %e, Std: %e ", min(b), max(b), mean(b), std(b) ) ] );
t1b = kstest(b);
t2b = adtest(b);
t3b = jbtest(b);
disp( [ sprintf(" ktest: %d, adtest: %d, jbtest: %d ", t1b, t2b, t3b ) ] );
% remove nan and inf
b = b( ~isnan( b ) );
b = b( ~isinf( b ) );
b = double(b);
disp( [ sprintf(" Mean: %e, Std: %e ", mean( b ), std( b ) ) ] );
c = rand(size_x, size_y, "single");
disp( [ sprintf("Var c Min: %e, Max: %e, Mean: %e, Std: %e ", min(c), max(c), mean(c), std(c) ) ] );
t1c = kstest(c);
t2c = adtest(c);
t3c = jbtest(c);
disp( [ sprintf(" ktest: %d, adtest: %d, jbtest: %d ", t1c, t2c, t3c ) ] );
c = c( ~isnan( c ) );
c = c( ~isinf( c ) );
c = double(c);
disp( [ sprintf(" Mean: %e, Std: %e ", mean( c ), std( c ) ) ] );
Var a Min: 5.572502e+29, Max: 3.402823e+38, Mean: Inf, Std: Inf
ktest: 1, adtest: 1, jbtest: 1
Mean: 1.701168e+38, Std: 9.822943e+37
Var b Min: -3.402817e+38, Max: 3.402815e+38, Mean: NaN, Std: NaN
ktest: 1, adtest: 1, jbtest: 1
Mean: 2.128702e+32, Std: 1.878338e+37
Var c Min: 1.400272e-09, Max: 9.999999e-01, Mean: 4.999776e-01, Std: 2.886557e-01
Warning: P is less than the smallest tabulated value, returning 0.0005.
> In adtest (line 279)
In SP_rand (line 32)
ktest: 1, adtest: 1, jbtest: 1
Mean: 4.999774e-01, Std: 2.886558e-01
Firan Lucian
Firan Lucian 2019년 7월 24일
Another issue of variant a and c (simple rand) is that it does not output negative numbers.
Var a Min: 5.023105e+30, Max: 3.402823e+38, Mean: Inf, Std: Inf
rel count: is+ 100.00, is- 0.00, is0 0.00, Inf 0.000000e+00, NAN 0.000000e+00
ktest: 1, adtest: 1, jbtest: 1
Mean: 1.701390e+38, Std: 9.822651e+37
Var b Min: -3.402819e+38, Max: Inf, Mean: NaN, Std: NaN
rel count: is+ 49.80, is- 49.81, is0 0.00, Inf 0.000000e+00, NAN 0.000000e+00
ktest: 1, adtest: 1, jbtest: 1
Mean: 6.011336e+32, Std: 1.879643e+37
Var c Min: 2.171108e-08, Max: 9.999999e-01, Mean: 4.999863e-01, Std: 2.886831e-01
rel count: is+ 100.00, is- 0.00, is0 0.00, Inf 0.000000e+00, NAN 0.000000e+00
Warning: P is less than the smallest tabulated value, returning 0.0005.
> In adtest (line 279)
In SP_rand (line 52)
ktest: 1, adtest: 1, jbtest: 1
Mean: 4.999864e-01, Std: 2.886831e-01
ipositif = sum(sign(a(:))==1) /size_x * 100;
inegatif = sum(sign(a(:))==-1) /size_x * 100;
izero = sum(a(:)==0) /size_x * 100;
iinf = sum(isinf(a)) /size_x * 100;
inan = sum(isnan(a)) /size_x * 100;
disp( [ sprintf(" rel count: is+ %.02f, is- %.02f, is0 %.02f, Inf %e, NAN %e", ipositif, inegatif, izero, iinf, inan ) ] );

댓글을 달려면 로그인하십시오.


Walter Roberson
Walter Roberson 2019년 7월 25일
For restricting to finite normalized values, randi() from the decimal representation of the smallest positive normalized number, to the largest positive normalized number times 2 plus 1. Values up to the largest positive get converted directly with typecast to single. Map the next group to negative by reflection and typecast and multiply by -1. The final upper value you map to 0 exactly.
This method should not require any rejection.

Firan Lucian
Firan Lucian 2019년 8월 5일
Using bitwise operations:
>> rand_SP_real
init 00000000000000000000000000000000
gen exponent 00010101
bitshift exponent 00001010100000000000000000000000
Add exponent 00001010100000000000000000000000
gen mantissa 00111010100111100011110
Add mantissa 00001010100111010100111100011110
ans =
single
1.5148e-32
>> rand_SP_real
init 00000000000000000000000000000000
Add negative sign 10000000000000000000000000000000
gen exponent 00100111
bitshift exponent 00010011100000000000000000000000
Add exponent 10010011100000000000000000000000
gen mantissa 11010011011010001011110
Add mantissa 10010011111010011011010001011110
ans =
single
-5.8995e-27
>> rand_SP_real
init 00000000000000000000000000000000
Add negative sign 10000000000000000000000000000000
gen exponent 11111111
bitshift exponent 01111111100000000000000000000000
Add exponent 11111111100000000000000000000000
gen mantissa 00010100000000110100111
Add mantissa 11111111100010100000000110100111
ans =
single
NaN
function [out] = rand_SP_real()
% generate a random SP number
% does generate zero, subnormals, nan, inf
%full range
% sp_sign = randi(2^1)-1;
% sp_exp = randi(2^8)-1;
% sp_mant = randi(2^23)-1;
sp = uint32(0);
disp(['init ' dec2bin(sp, 32) ]);
if randi(2^1) == 2
%negative sign
sp = uint32(bitset(sp, 32));
disp(['Add negative sign ' dec2bin(sp, 32)]);
end
sp_exp = randi(2^8)-1;
disp(['gen exponent ' dec2bin(sp_exp, 8)]);
% bitshift exponent
sp_exp = uint32(bitshift(sp_exp, 23, 'uint32'));
disp(['bitshift exponent ' dec2bin(sp_exp, 32)]);
% add exponent
sp = uint32(sp + sp_exp) ;
disp(['Add exponent ' dec2bin(sp, 32)]);
% rand mantissa
sp_mant = randi(2^23)-1;
disp(['gen mantissa ' dec2bin(sp_mant, 23)]);
sp = uint32(sp + sp_mant) ;
disp(['Add mantissa ' dec2bin(sp, 32) ]);
% convert to single
out = typecast( uint32(sp), 'single');
end
>> rand_SP_real_0normals
init 00000000000000000000000000000000
Add negative sign 10000000000000000000000000000000
gen exponent 10110010
bitshift exponent 01011001000000000000000000000000
Add exponent 11011001000000000000000000000000
gen mantissa 11100100000100100011110
Add mantissa 11011001011100100000100100011110
ans =
single
-4.2579e+15
function [out] = rand_SP_real_0normals()
% generate a random SP normal number and zero
% does not generate subnormals, nan, inf
%full range
% sp_sign = randi(2^1)-1;
% sp_exp = randi(2^8)-1;
% sp_mant = randi(2^23)-1;
sp = uint32(0);
disp(['init ' dec2bin(sp, 32) ]);
if randi(2^1) == 2
%negative sign
sp = uint32(bitset(sp, 32));
disp(['Add negative sign ' dec2bin(sp, 32)]);
end
% remove inf nan from exponent (-1)
sp_exp = randi(2^8-1)-1;
% remove subnormals (denormalized), if exponent == 0
if sp_exp ~= 0
disp(['gen exponent ' dec2bin(sp_exp, 8)]);
% bitshift exponent
sp_exp = uint32(bitshift(sp_exp, 23, 'uint32'));
disp(['bitshift exponent ' dec2bin(sp_exp, 32)]);
% add exponent
sp = uint32(sp + sp_exp) ;
disp(['Add exponent ' dec2bin(sp, 32)]);
% rand mantissa
sp_mant = randi(2^23)-1;
disp(['gen mantissa ' dec2bin(sp_mant, 23)]);
sp = uint32(sp + sp_mant) ;
disp(['Add mantissa ' dec2bin(sp, 32) ]);
end
% convert to single
out = typecast( uint32(sp), 'single');
end
>> rand_SP_real_infnan
init 00000000000000000000000000000000
gen exponent 11111111
bitshift exponent 01111111100000000000000000000000
Add exponent 01111111100000000000000000000000
gen mantissa 00011011010011011001100
Add mantissa 01111111100011011010011011001100
ans =
single
NaN
>> rand_SP_real_infnan
init 00000000000000000000000000000000
Add negative sign 10000000000000000000000000000000
gen exponent 11111111
bitshift exponent 01111111100000000000000000000000
Add exponent 11111111100000000000000000000000
gen mantissa 00000001001011111011010
Add mantissa 11111111100000001001011111011010
ans =
single
NaN
function [out] = rand_SP_real_infnan()
% generate a random SP number in nan range and inf
% does generate subnormals, normals, zero
%full range
% sp_sign = randi(2^1)-1;
% sp_exp = randi(2^8)-1;
% sp_mant = randi(2^23)-1;
sp = uint32(0);
disp(['init ' dec2bin(sp, 32) ]);
if randi(2^1) == 2
%negative sign
sp = uint32(bitset(sp, 32));
disp(['Add negative sign ' dec2bin(sp, 32)]);
end
% only one exponent
sp_exp = 255;
disp(['gen exponent ' dec2bin(sp_exp, 8)]);
% bitshift exponent
sp_exp = uint32(bitshift(sp_exp, 23, 'uint32'));
disp(['bitshift exponent ' dec2bin(sp_exp, 32)]);
% add exponent
sp = uint32(sp + sp_exp) ;
disp(['Add exponent ' dec2bin(sp, 32)]);
% rand mantissa
sp_mant = randi(2^23)-1;
disp(['gen mantissa ' dec2bin(sp_mant, 23)]);
sp = uint32(sp + sp_mant) ;
disp(['Add mantissa ' dec2bin(sp, 32) ]);
% convert to single
out = typecast( uint32(sp), 'single');
end
>> rand_SP_real_subnormals0
init 00000000000000000000000000000000
Add negative sign 10000000000000000000000000000000
gen exponent 00000000
bitshift exponent 00000000000000000000000000000000
Add exponent 10000000000000000000000000000000
gen mantissa 11010001001110101100100
Add mantissa 10000000011010001001110101100100
ans =
single
-9.6074e-39
function [out] = rand_SP_real_subnormals0()
% generate a random SP number in subnormals range and zero
% does generate normals, nan, inf range
%full range
% sp_sign = randi(2^1)-1;
% sp_exp = randi(2^8)-1;
% sp_mant = randi(2^23)-1;
sp = uint32(0);
disp(['init ' dec2bin(sp, 32) ]);
if randi(2^1) == 2
%negative sign
sp = uint32(bitset(sp, 32));
disp(['Add negative sign ' dec2bin(sp, 32)]);
end
% only one exponent
sp_exp = 0;
disp(['gen exponent ' dec2bin(sp_exp, 8)]);
% bitshift exponent
sp_exp = uint32(bitshift(sp_exp, 23, 'uint32'));
disp(['bitshift exponent ' dec2bin(sp_exp, 32)]);
% add exponent
sp = uint32(sp + sp_exp) ;
disp(['Add exponent ' dec2bin(sp, 32)]);
% rand mantissa
sp_mant = randi(2^23)-1;
disp(['gen mantissa ' dec2bin(sp_mant, 23)]);
sp = uint32(sp + sp_mant) ;
disp(['Add mantissa ' dec2bin(sp, 32) ]);
% convert to single
out = typecast( uint32(sp), 'single');
end
  댓글 수: 4
James Tursa
James Tursa 2019년 8월 12일
You need to check for 0 also, since 0 is not a denorm.
Steven Lord
Steven Lord 2019년 8월 12일
As soon as I saw there was a comment on this question I realized the point James made. Yes, you need to exclude 0.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Characters and Strings에 대해 자세히 알아보기

제품


릴리스

R2018b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by