How can I generate random single precision (float32) numbers ?
Show older comments
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 Comments
Bruno Luong
on 25 Jul 2019
Just curious, what you are trying to do with such randomize process?
The distribution of numbers will be complex on the real axis measurement.
If you do some study downstream you might wonder the effect of that at first.
Firan Lucian
on 25 Jul 2019
- write one and upload it to FEX.
- make a feature request: https://www.mathworks.com/matlabcentral/answers/460998-how-can-i-make-a-features-request
Bruno Luong
on 25 Jul 2019
Edited: Bruno Luong
on 25 Jul 2019
It is still not clear to me the pertinent of such thing.
I was teached to manipulate random variable with some "meaningful" probability distribution, uniform on finite interval, gaussian noise, poisson, gamma, ... you name it. Something that is at least integrable and have few low-order moment well defined.
Generate such distribution related to the binary coding mantissa/exponent,sign of 4-bytes floating number?
You 'll get likely generation of many huge numbers that does appear anywhere in practice life or computer reality. That is my prediction.
Firan Lucian
on 25 Jul 2019
Guillaume
on 26 Jul 2019
I would echo bruno's question. What's the purpose of this? Since matlab is a high-level language the stated reasons don't seem very pertinent. Matlab makes no statement about types of NaN, so even you generate a qnan or an snan, there's no guarantee that it'll be preserved. Similarly, with +/- zero. While negative zeros sometimes appear, again the documentation has nothing to say about them, so it's really an implementation detail, subject to change. Same with rounding modes, matlab does not give you control over the fp rounding modes.
Note: "A signalling NaN (NANS) is represented by [...] A quiet NaN (NANQ) is represented by [...]" vs "on different chips with specific ALU functionality"
According to wikipedia, "Encodings of qNaN and sNaN are not completely specified in IEEE 754 and depend on the processor"
Walter Roberson
on 26 Jul 2019
MATLAB preserves the sign of 0:
>> a=-0;1/(a*5)
ans =
-Inf
There just are not many cases where it matters.
MATLAB only preserves exact nans for direct assignment. Many of the routines that have to be nan aware end up generating new nans instead of copying the existing ones. For example, sort() of a vector involving nans will generate new nans instead of carefully keeping track of the kind of nan that was input.
The support for rounding modes is undocumented: https://stackoverflow.com/questions/55682034/how-to-change-the-rounding-mode-for-floating-point-operations-in-matlab
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.
Walter Roberson
on 27 Jul 2019
https://www.advanpix.com/2016/04/28/branch-cuts-and-signed-zeros-in-matlab/ talks about some difficulties with MATLAB negative 0s.
Answers (5)
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 Comments
Firan Lucian
on 24 Jul 2019
"any thoughts on that ?"
One straighforward solution would be to generate hex strings from the entire number range, and then use set or logical comparisons to detect the unwanted hex digits/combinations, and then remove those "numbers". As there are only a few special cases this would not be so difficult.
Walter Roberson
on 25 Jul 2019
hex2num() to do the conversion from char to numeric.
"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:
https://www.mathworks.com/matlabcentral/answers/92132-how-can-i-create-a-single-precision-number-given-an-ascii-hex-string-in-matlab-7-8-r2009a#answer_101483
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.
Walter Roberson
on 25 Jul 2019
Ah good point.
James Tursa
on 24 Jul 2019
Edited: James Tursa
on 26 Jul 2019
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 Comments
Walter Roberson
on 26 Jul 2019
It works! ;-)
Firan Lucian
on 31 Jul 2019
Bruno Luong
on 31 Jul 2019
That comes down more or less to specify the probability distribution you want to generate.
Firan Lucian
on 5 Aug 2019
Edited: Firan Lucian
on 5 Aug 2019
Guillaume
on 5 Aug 2019
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
on 6 Aug 2019
the cyclist
on 24 Jul 2019
Edited: the cyclist
on 24 Jul 2019
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 Comments
Due to the precision of rand's output, scaling those values will miss some numbers, e.g.:
>> one = single(1);
>> rmx = realmax('single');
>> A = rmx*(one) % largest
A =
3.4028235e+38
>> B = rmx*(one-eps(one)) % scaled second largest
B =
3.4028231e+38
>> C = rmx-eps(rmx) % missed second largest
C =
3.4028233e+38
>> num2hex(A) % largest
ans =
7f7fffff
>> num2hex(B) % scaled second largest
ans =
7f7ffffd
>> num2hex(C) % missed second largest
ans =
7f7ffffe
Firan Lucian
on 24 Jul 2019
Firan Lucian
on 24 Jul 2019
Walter Roberson
on 25 Jul 2019
0 votes
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
on 5 Aug 2019
4 Comments
Firan Lucian
on 12 Aug 2019
Steven Lord
on 12 Aug 2019
There's no need to convert to uint32 and call hex2dec. Just use realmin directly. As a bonus, this will work for either double or single inputs.
function tf = isSubnormal(value)
smallestPositiveNormalized = realmin(class(value));
tf = abs(value) < smallestPositiveNormalized;
end
James Tursa
on 12 Aug 2019
You need to check for 0 also, since 0 is not a denorm.
Steven Lord
on 12 Aug 2019
As soon as I saw there was a comment on this question I realized the point James made. Yes, you need to exclude 0.
Categories
Find more on Numeric Types in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!