Эвристика, чтобы определить, являются ли целые 4 байта данных целыми числами или плавают

Какую лучшую эвристику я могу использовать, чтобы определить, является ли блок X 4-байтов целыми числами или поплавками? Человек может сделать это легко, но я хотел сделать это программно.

Я понимаю, что, поскольку каждая комбинация битов приведет к действительному целому числу и (почти?), все из них также приведут к действительному поплату, нет никакого способа узнать наверняка. Но я все же хотел бы определить наиболее вероятного кандидата (который будет практически всегда правильным, или, по крайней мере, человек может это сделать).

Например, возьмите серию 4-байтовых исходных данных и сначала напечатайте их как целые числа, а затем как float:

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: Я использую С++, но вы можете отвечать на любом языке, псевдокоде или просто на английском языке.

Ответ 1

Вы будете смотреть на верхние 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, тогда это значение либо бесконечно, либо 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

Показатель всех 1s представляет бесконечность. Вероятно, вы не найдете бесконечности в своих данных, но знаете, что лучше, чем я. - Infinity - 0xFF800000, + Infinity - 0x7F800000, любое значение, отличное от 0 в мантиссе Бесконечности, искажено. неверно развитые бесконечности используются как NaN.

Загрузка NaN в регистр с плавающей точкой может привести к тому, что он выдаст исключение, поэтому вы хотите использовать целочисленную математику, чтобы угадать, являются ли ваши данные float или int, пока вы не уверены, что это int.

Ответ 2

Эвристика "здравого смысла" из вашего примера, по-видимому, в основном сводится к проверке диапазона. Если одна интерпретация очень велика (или крошечная фракция, близкая к нулю), это, вероятно, неверно. Проверьте экспоненту интерпретации float и сравните ее с показателем, результатом которого является правильный статический приведение целочисленной интерпретации к float.

Ответ 3

Похож на проблему колмогоровская проблема. В основном, из того, что вы показываете в качестве примера, более короткий номер (когда он печатается как строка, которую должен читать человек), будь то целым или плавающим, является правильным ответом для вашей эвристики.

Кроме того, очевидно, что если значение является неправильным значением float, оно является целым числом: -)

Кажется, достаточно прямо для реализации.

Ответ 4

Вы можете, вероятно, "обнаружить" его, посмотрев на старшие биты, с поплавками, которые они обычно не равны нулю, с целыми числами, они будут, если вы не имеете дело с очень большим числом. Итак... вы можете попробовать и посмотреть, возвращается ли (2^30) & number 0 или нет.

Ответ 5

Если оба числа положительны, ваши поплавки достаточно велики (больше 10 ^ -42), и ваши ints достаточно малы (меньше 8 * 10 ^ 6), тогда проверка довольно проста. Обработайте данные как float и сравните с наименее нормированным поплавком.

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.

Ответ 6

Человек может легко это сделать

Человек не может этого сделать вообще. Эрго и компьютер не могут. Имеются 2 ^ 32 действительных значения int. Большое количество из них также являются действительными значениями float. Невозможно отличить намерение данных, кроме как пометить его или не попасть в такой беспорядок в первую очередь.

Не пытайтесь это сделать.

Ответ 7

Если вы знаете, что ваши поплавки все будут фактическими (никакие NaN, INF, денормалы или другие аберрантные значения), вы можете использовать этот критерий. В общем случае массив из int будет иметь высокую вероятность содержать "плохие" значения float.

Ответ 8

Я предполагаю следующее:

  • что вы имеете в виду числа с плавающей точкой с одиночной точностью IEEE 754.
  • что бит знака поплавка сохраняется в MSB int.

Итак, идем:

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;
}

Ответ 9

упростив то, что сказал Алан, я ТОЛЬКО посмотрел на целочисленную форму. и скажем, если число больше 99999999, то это почти определенно плавающий.

Это имеет то преимущество, что это быстро, легко и позволяет избежать проблем нации.

У него есть тот недостаток, что он довольно много дерьмо... я на самом деле не смотрел, какие плавающие они будут представлять или что-то еще, но это выглядит разумно из ваших примеров...

В любом случае, это эвристика, поэтому она будет полна дерьма и не всегда будет работать в любом случае...

Измерьте с помощью микрометра, отметьте мелом, вырежьте топор.

Ответ 10

Вот эвристика, с которой я столкнулся, исходя из идеи @kriss. После краткого взгляда на некоторые из моих данных, это, кажется, работает достаточно хорошо.

Я использую его в дизассемблере, чтобы определить, было ли 32-битное значение, скорее всего, целым или плавающим литералом.

public class FloatUtil {
    private static final int canonicalFloatNaN = Float.floatToRawIntBits(Float.NaN);
    private static final int maxFloat = Float.floatToRawIntBits(Float.MAX_VALUE);
    private static final int piFloat = Float.floatToRawIntBits((float)Math.PI);
    private static final int eFloat = Float.floatToRawIntBits((float)Math.E);

    private static final DecimalFormat format = new DecimalFormat("0.####################E0");

    public static boolean isLikelyFloat(int value) {
        // Check for some common named float values
        if (value == canonicalFloatNaN ||
                value == maxFloat ||
                value == piFloat ||
                value == eFloat) {
            return true;
        }

        // Check for some named integer values
        if (value == Integer.MAX_VALUE || value == Integer.MIN_VALUE) {
            return false;
        }

        // a non-canocical NaN is more likely to be an integer
        float floatValue = Float.intBitsToFloat(value);
        if (Float.isNaN(floatValue)) {
            return false;
        }

        // Otherwise, whichever has a shorter scientific notation representation is more likely.
        // Integer wins the tie
        String asInt = format.format(value);
        String asFloat = format.format(floatValue);

        // try to strip off any small imprecision near the end of the mantissa
        int decimalPoint = asFloat.indexOf('.');
        int exponent = asFloat.indexOf("E");
        int zeros = asFloat.indexOf("000");
        if (zeros > decimalPoint && zeros < exponent) {
            asFloat = asFloat.substring(0, zeros) + asFloat.substring(exponent);
        } else {
            int nines = asFloat.indexOf("999");
            if (nines > decimalPoint && nines < exponent) {
                asFloat = asFloat.substring(0, nines) + asFloat.substring(exponent);
            }
        }

        return asFloat.length() < asInt.length();
    }
}

И вот некоторые из значений, для которых он работает (и пары это не так)

@Test
public void isLikelyFloatTest() {
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1.23f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1.0f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(Float.NaN)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(Float.NEGATIVE_INFINITY)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(Float.POSITIVE_INFINITY)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1e-30f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1000f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(-1f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(-5f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1.3333f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(4.5f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(.1f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(50000f)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(Float.MAX_VALUE)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits((float)Math.PI)));
    Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits((float)Math.E)));

    // Float.MIN_VALUE is equivalent to integer value 1. this should be detected as an integer
    // Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(Float.MIN_VALUE)));

    // This one doesn't quite work. It has a series of 2 0's, but we only strip 3 0 or more
    // Assert.assertTrue(FloatUtil.isLikelyFloat(Float.floatToRawIntBits(1.33333f)));

    Assert.assertFalse(FloatUtil.isLikelyFloat(0));
    Assert.assertFalse(FloatUtil.isLikelyFloat(1));
    Assert.assertFalse(FloatUtil.isLikelyFloat(10));
    Assert.assertFalse(FloatUtil.isLikelyFloat(100));
    Assert.assertFalse(FloatUtil.isLikelyFloat(1000));
    Assert.assertFalse(FloatUtil.isLikelyFloat(1024));
    Assert.assertFalse(FloatUtil.isLikelyFloat(1234));
    Assert.assertFalse(FloatUtil.isLikelyFloat(-5));
    Assert.assertFalse(FloatUtil.isLikelyFloat(-13));
    Assert.assertFalse(FloatUtil.isLikelyFloat(-123));
    Assert.assertFalse(FloatUtil.isLikelyFloat(20000000));
    Assert.assertFalse(FloatUtil.isLikelyFloat(2000000000));
    Assert.assertFalse(FloatUtil.isLikelyFloat(-2000000000));
    Assert.assertFalse(FloatUtil.isLikelyFloat(Integer.MAX_VALUE));
    Assert.assertFalse(FloatUtil.isLikelyFloat(Integer.MIN_VALUE));
    Assert.assertFalse(FloatUtil.isLikelyFloat(Short.MIN_VALUE));
    Assert.assertFalse(FloatUtil.isLikelyFloat(Short.MAX_VALUE));
}