Сколько уникальных значений существует от 0 до 1 стандартного поплавка?
Я предполагаю, что другой способ формулировки этого вопроса состоит в том, что десятичное место вы можете использовать при использовании float
, который будет только от 0 до 1?
Я попытался его обработать, посмотрев MSDN. Который говорит, что точность составляет 7 цифр. Я думал, что это означает, что он может отслеживать изменения 0.0000001
.
Однако, если я это сделаю:
float test = 0.00000000000000000000000000000000000000000001f;
Console.WriteLine(test);
Он выписывает 9.949219E-44
Если я добавлю больше нулей, он выведет 0
.
Я уверен, что здесь что-то отсутствует, поскольку эта степень точности кажется массово неправильной. В основном, как float размером 32 бита, и только от 0-1 на этом уровне точности содержит 1e + 44 возможных номеров...
Ответы
Ответ 1
Сколько уникальных значений существует от 0 до 1 стандартного поплавка?
На самом деле это не вопрос, на который вы хотите получить ответ, но ответ состоит в том, что не включая 0
и 1
себя, что в этом диапазоне есть 2**23 - 1
субнормальные числа и 126 * 2**23
нормальные числа, для всего 127 * 2**23 - 1
или 1,065,353,215
.
Но учтите, что эти числа не равномерно распределены на интервале между 0
и 1
. Использование "дельта" 1f / 1065353215f
в цикле от 0f
до 1f
не будет работать для вас.
Если вы хотите перейти от 0.0 до 1.0 с помощью экваториальных шагов (десятичной) формы 0.00... 01, возможно, вы должны использовать decimal
вместо float
. Он будет точно представлять цифры.
Если вы придерживаетесь float
, попробуйте с 0.000001
(в десять раз больше, чем предлагаемое значение), но обратите внимание, что ошибки могут возникать при выполнении очень многих дополнений с не представимым числом.
Также обратите внимание: Существует несколько доменов, в которых вы не можете даже рассчитывать на первые семь значащих десятичных цифр float
. Попробуйте, например, сохранить значение 0.000986f
или 0.000987f
переменной float
(убедитесь, что оптимизация не содержит значения в "более широком" месте хранения) и выпишите эту переменную. Первые семь цифр не идентичны 0.0009860000
соответственно. 0.0009870000
. Снова вы можете использовать decimal
, если вы хотите работать с числами, десятичные разложения которых "коротки".
Изменить: Если вы можете использовать "двоичный" шаг для своего цикла, попробуйте:
float delta = (float)Math.Pow(2, -24);
или эквивалентно как литерал:
const float delta = 5.96046448e-8f;
Хорошая вещь об этой дельте состоит в том, что все значения, которые вы получаете через цикл, точно представлены в вашем float
. Как раз перед (под) 1f
, вы будете делать как можно меньше возможных шагов для этой величины.
Ответ 2
Это 7 значащих цифр, то есть при записи в экспоненциальной нотации вы игнорируете экспонента.
0.0000000000000000001234567 имеет такое же количество значащих цифр, что и 12345670000000000000, только с другим показателем. Это волшебство, которое позволяет номерам с плавающей запятой хранить действительно маленькие и действительно большие числа.
Что касается точно количества возможных чисел для float
в (0, 1), я не могу сказать точно прямо сейчас. У вас есть 23-битная мантисса, поэтому 2 23 возможные состояния. Тогда есть 8-битный показатель, и если я не ошибаюсь, примерно половина его возможных значений приведет к числу от 0 до 1. Который должен оставить вас примерно с 2 23 + 7= 2 30 возможных значений в этом диапазоне. Если что-нибудь, что возможно, верхняя граница, а не точное значение. Мне нужно будет проконсультироваться с документацией о более тонких деталях, чтобы точно знать (и, возможно, пересмотреть математику выше, которая может пропустить несколько точек).
Ответ 3
Я написал эту очень глупую программу, и она дает ответ 1065353217
, который действительно просто застенчив 2 30 (1073741824). Вычитайте 2 из этого числа, если вы искали все числа, не содержащие 0 и 1. Кстати, наименьшее ненулевое число выглядит как 1.401298E-45.
class Program
{
public unsafe static int Search()
{
int i = 0;
float* f = (float*)&i;
while (*f >= 0 && *f <= 1)
{
f = (float*)&i;
i++;
}
return i;
}
static void Main(string[] args)
{
Console.WriteLine(Search());
Console.ReadLine();
}
}
Ответ 4
Положительные значения с плавающей запятой упорядочены так же, как и их кодировки. 0.0f
- 0x00000000
. 1.0f
- 0x3f800000
. Таким образом, существуют значения 0x3f800000 - 1
с плавающей запятой, которые лежат строго между ними, или 1,065,353,215.
Если вы хотите включить конечные точки в свой счет, имейте в виду, что есть два кодировки нуля.
Имейте в виду, что значения с плавающей запятой не равномерно распределены. Разница между 1.0f
и следующим меньшим числом равна 2**-24
, тогда как разница между 0.0f
и следующим большим числом 2**-149
. Если вы хотите увеличивать поплавок от 0 до 1 с помощью единых шагов, наименьший размер шага, который вы можете использовать, - 2**-24
.
Ответ 5
Поскольку тип с плавающей запятой имеет 4 байтовых данных, мы можем проверить все возможные варианты всех 4 байтов (0-255) с помощью приведенного ниже кода и подсчитать, сколько из них находится в диапазоне [0,1] - включительно
Таким образом, ответ будет 1065353218
Постскриптум Выполнение кода может занять до 2-3 минут в зависимости от компьютера.
public static void Main()
{
long count = 0;
//byte checking sub/method
void CheckThisBytes(byte ii, byte jj, byte kk, byte ll)
{
var data = new[] {ii, jj, kk, ll};
var f = BitConverter.ToSingle(data, 0);
//is f in range ?
if (f >= 0.0 && f <= 1.0)
{
count++;
}
}
const int max = 255;
// generate all possible cases
for (var i = 0; i <= max; i++)
{
for (var j = 0; j <= max; j++)
{
for (var k = 0; k <=max; k++)
{
for (var l = 0; l <= max; l++)
{
//check if current float is in range
CheckThisBytes((byte) i, (byte) j, (byte) k, (byte) l);
}
}
}
}
Console.WriteLine("\n Count:" + count);
Console.ReadLine();
//result will be 1065353218
}