Этот результат меня не удивляет, учитывая, как представлены числа с плавающей запятой. Предположим, у нас есть очень короткий тип с плавающей точкой с точностью всего 4 бита. Если бы мы сгенерировали случайное число от 0 до 1, распределенное равномерно, было бы 16 возможных значений:
0.0000
0.0001
0.0010
0.0011
0.0100
...
0.1110
0.1111
Если так они выглядели в машине, вы могли бы протестировать младший бит, чтобы получить распределение 50/50. Однако числа с плавающей запятой IEEE представлены как степень, умноженная на 2 мантиссы; одно поле в поплавке - степень двойки (плюс фиксированное смещение). Степень двойки выбрана таким образом, чтобы «мантисса» всегда была числом> = 1,0 и <2,0. Это означает, что, по сути, числа, отличные от этих 0.0000
, будут представлены следующим образом:
0.0001 = 2^(-4) x 1.000
0.0010 = 2^(-3) x 1.000
0.0011 = 2^(-3) x 1.100
0.0100 = 2^(-2) x 1.000
...
0.0111 = 2^(-2) x 1.110
0.1000 = 2^(-1) x 1.000
0.1001 = 2^(-1) x 1.001
...
0.1110 = 2^(-1) x 1.110
0.1111 = 2^(-1) x 1.111
( 1
Перед двоичной точкой подразумевается значение; для 32- и 64-разрядных чисел с плавающей запятой фактически не выделяется бит для хранения этого значения 1
.)
Но, глядя на приведенное выше, следует продемонстрировать, почему, если вы преобразуете представление в биты и посмотрите на младший бит, вы получите ноль в 75% случаев. Это связано с тем, что все значения меньше 0,5 (двоичные 0.1000
), что составляет половину возможных значений, имеют смещенные мантиссы, в результате чего в младшем бите появляется 0. Ситуация по существу такая же, когда мантисса имеет 52 бита (не считая подразумеваемой 1), как и double
у.
(На самом деле, как @sneftel предложил в комментарии, мы могли бы включить в распределение более 16 возможных значений, генерируя:
0.0001000 with probability 1/128
0.0001001 with probability 1/128
...
0.0001111 with probability 1/128
0.001000 with probability 1/64
0.001001 with probability 1/64
...
0.01111 with probability 1/32
0.1000 with probability 1/16
0.1001 with probability 1/16
...
0.1110 with probability 1/16
0.1111 with probability 1/16
Но я не уверен, что такое распространение ожидает большинство программистов, поэтому, вероятно, это не стоит того. Кроме того, это не очень выгодно, когда значения используются для генерации целых чисел, как это часто бывает со случайными значениями с плавающей запятой.)