Что лучшей является эвристика, которую я могу использовать, чтобы определить, является ли блок X 4 байтов целыми числами или плаваниями? Человек может сделать это легко, но я хотел сделать это программно.
Я понимаю, что начиная с каждой комбинации битов приведет к допустимому целому числу и (почти?) все они также приведут к допустимому плаванию, нет никакого способа знать наверняка. Но я все еще хотел бы идентифицировать наиболее вероятного кандидата (который фактически всегда будет корректен; или по крайней мере, человек может сделать это).
Например, давайте возьмем серию 4-байтовых необработанных данных и распечатаем их как целые числа сначала и затем как плавания:
1 1.4013e-45 10 1.4013e-44 44 6.16571e-44 5000 7.00649e-42 1024 1.43493e-42 0 0 0 0 -5 -nan 11 1.54143e-44
Очевидно, они будут целыми числами.
Теперь, другой пример:
1065353216 1 1084227584 5 1085276160 5.5 1068149391 1.33333 1083179008 4.5 1120403456 100 0 0 -1110651699 -0.1 1195593728 50000
Они, очевидно, будут плаваниями.
PS: я использую C++, но можно ответить на любом языке, псевдо коде или только на английском языке.
Вы собираетесь смотреть на верхние 8 или 9 бит. Вот где находятся знак и мантисса значения с плавающей запятой. Значения 0x00 0x80 и 0xFF здесь довольно необычны для действительных данных с плавающей запятой.
В частности, если все верхние 9 битов равны 0, то это, вероятно, будет действительным значением с плавающей запятой только в том случае, если все 32 бита равны 0. Другой способ сказать это: если показатель степени равен 0, мантисса также должна быть равна нулю. . Если верхний бит равен 1, а следующие 8 бит - 0, это допустимо, но также маловероятно. Он представляет -0.0, что является допустимым значением с плавающей запятой, но бессмысленным.
Чтобы выразить это в числовом выражении. если старший байт равен 0x00 (или 0x80), то значение имеет величину максимум 2.35e-38. Постоянная Планка составляет 6,62e-34 м2 кг / с, что на 4 порядка больше. Предполагаемый диаметр протона намного больше (оценивается в 1,6e − 15 метров). Наименьшее ненулевое значение для аудиоданных составляет около 2,3e-10. Вы вряд ли увидите, что значения с плавающей запятой являются законными измерениями чего-либо реального, меньшего, чем 2.35e-38, но не нуля.
В другом направлении, если старший байт равен 0xFF, то это значение либо Infinite, либо NaN, либо больше по величине, чем 3,4e + 38. Возраст Вселенной оценивается в 1,3e + 10 лет (1,3e + 25 фемтосекунд). Наблюдаемая Вселенная имеет примерно e + 23 звезд, число Авагадро - 6.02e + 23.И снова значения с плавающей запятой больше, чем e + 38, редко появляются в достоверных измерениях.
Это не означает, что FPU не может загружать или выдавать такие значения, и вы обязательно увидите их в промежуточных значениях вычислений, если вы работаете с современными FPU. Современный FPU загружает значение с плавающей запятой, у которого показатель степени равен 0, но другие биты не равны 0. Они называются денормализованными значениями. Вот почему вы видите небольшие положительные целые числа, которые отображаются как значения с плавающей запятой в диапазоне e-42, хотя нормальный диапазон значений с плавающей запятой только опускается до e-38
Показатель всех единиц представляет бесконечность. Вы, вероятно, не найдете бесконечностей в своих данных, но вы бы знали лучше, чем я. -Infinity - это 0xFF800000, + Infinity - это 0x7F800000, любое значение, кроме 0 в мантиссе Infinity, искажено. уродливые бесконечности используются как NaN.
Загрузка NaN в регистр с плавающей запятой может вызвать исключение, поэтому вы хотите использовать целочисленную математику, чтобы угадать, являются ли ваши данные float или int, пока вы не будете достаточно уверены, что это int.
Похоже, проблема сложности колмогорова . По сути, из того, что вы показываете в качестве примера, более короткое число (при печати в виде строки для чтения человеком), будь то целое или с плавающей запятой, является правильным ответом для вашей эвристики.
Кроме того, очевидно, что если значение является неправильным с плавающей запятой, оно является целым числом: -)
Кажется достаточно прямым для реализации.
Вы, вероятно, сможете «обнаружить» это, посмотрев на старшие биты, с плавающей точкой они обычно не равны нулю, с целыми числами, если вы не имеете дело с очень большим числом. Итак ... вы можете попробовать и посмотреть, возвращает ли (2 ^ 30) & number
0
или нет.
Человек может сделать это легко
Человек вообще не может этого сделать. Ergo не может и компьютер. Имеется 2 ^ 32 допустимых значений типа int. Многие из них также являются допустимыми значениями с плавающей запятой. Нет другого способа отличить цель данных, кроме как пометить их или вообще не попасть в такой беспорядок.
Не пытайтесь это сделать.
Если вы знаете, что все ваши числа с плавающей запятой будут фактическими значениями (без NaN, INF, денормальных значений или других отклоняющихся значений), вы можете использовать этот критерий. Как правило, массив целых чисел с высокой вероятностью содержит «плохие» значения с плавающей запятой.
Если оба числа положительны, ваши числа с плавающей запятой достаточно большие ( больше 10 ^ -42), а ваши целые числа достаточно малы (меньше 8 * 10 ^ 6), то проверка довольно проста. Рассматривайте данные как число с плавающей запятой
и сравнивайте с наименее нормализованным числом с плавающей запятой.
union float_or_int {
float f;
int32_t i;
};
bool is_positive_normalized_float( float_or_int &u ) {
return u.f >= numeric_limits<float>::min();
}
Предполагается, что IEEE float
и одинаковая конечная скорость между CPU и FPU.
Эвристика "здравого смысла" из вашего примера, по-видимому, в основном сводится к проверке диапазона. Если одна интерпретация очень большая (или крошечная дробь, близкая к нулю), это, вероятно, неверно. Проверьте показатель степени интерпретации числа с плавающей запятой и сравните его с показателем степени, который является результатом правильного статического преобразования целочисленной интерпретации в число с плавающей запятой.
Упрощая то, что сказал Алан, я бы посмотрел ТОЛЬКО на целочисленную форму. и скажем, если число больше 99999999, то это почти наверняка число с плавающей запятой.
Это имеет то преимущество, что это быстро, легко и позволяет избежать проблем с нанотехнологиями.
У него есть недостаток в том, что он в значительной степени полон дерьма ... Я на самом деле не смотрел, какие поплавки они будут представлять или что-то в этом роде, но из ваших примеров это выглядит разумным ...
В любом случае, это это эвристика, так что ГОННА будет полная чушь, да и не всегда срабатывает ...
Измерьте микрометром, отметьте мелом, отрежьте топором.
Я предполагаю следующее:
Итак, начнем:
static boolean probablyFloat(uint32_t bits) {
bool sign = (bits & 0x80000000U) != 0;
int exp = ((bits & 0x7f800000U) >> 23) - 127;
uint32_t mant = bits & 0x007fffff;
// +- 0.0
if (exp == -127 && mant == 0)
return true;
// +- 1 billionth to 1 billion
if (-30 <= exp && exp <= 30)
return true;
// some value with only a few binary digits
if ((mant & 0x0000ffff) == 0)
return true;
return false;
}
int main() {
assert(probablyFloat(1065353216));
assert(probablyFloat(1084227584));
assert(probablyFloat(1085276160));
assert(probablyFloat(1068149391));
assert(probablyFloat(1083179008));
assert(probablyFloat(1120403456));
assert(probablyFloat(0));
assert(probablyFloat(-1110651699));
assert(probablyFloat(1195593728));
return 0;
}