启发式以识别一系列4字节数据块是整数还是浮点数

时间:2010-03-21 00:53:54

标签: c++ algorithm language-agnostic floating-point artificial-intelligence

什么是最好的启发式方法,我可以用来确定一个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 ++,但您可以使用任何语言,伪代码或英语进行回答。

10 个答案:

答案 0 :(得分:9)

你的例子中的“常识”启发式似乎基本上等于范围检查。如果一个解释非常大(或一小部分,接近于零),那可能是错误的。检查浮点解释的指数,并将其与整数解释的适当静态强制转换为浮点数的指数进行比较。

答案 1 :(得分:4)

看起来像kolmogorov complexity问题。基本上,从你所展示的例子中,较短的数字(当被打印为人类阅读的字符串时),无论是整数还是浮点数,都是启发式的正确答案。

此外,显然如果值是一个不正确的浮点数,它是一个整数: - )

似乎足以直接实施。

答案 2 :(得分:1)

你可以通过查看高位来检测它,浮点数通常是非零的,带有整数,除非你处理的是非常大的数字,否则它们就会被检测到。所以......您可以尝试查看(2^30) & number是否返回0

答案 3 :(得分:1)

如果两个数字都是正数,那么您的浮点数相当大(大于10 ^ -42),并且您的整数相当小(小于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之间的相同内容。

答案 4 :(得分:1)

  

人类可以轻松地做到这一点

人类根本无法做到。 Ergo也不能用电脑。有2 ^ 32个有效的int值。其中大量也是有效的浮点值。除了标记数据之外,没有办法区分数据的意图,或者首先不会陷入如此混乱的状态。

不要尝试这个。

答案 5 :(得分:0)

您将要查看高8位或9位。这就是浮点值的符号和尾数。对于有效的浮点数据,这里的值0x00 0x80和0xFF非常罕见。

特别是如果高9位全部为0,则只有当所有32位都为0时,这可能是有效的浮点值。另一种说法是,如果指数为0,则尾数也应为零。如果高位为1且接下来的8位为0,则这是合法的,但也不可能有效。它代表-0.0这是一个合法的浮点值,但是没有意义。

将其用于数字术语。如果高位字节是0x00(或0x80),则该值的大小最多 2.35e-38。普朗克常数为6.62e-34 m2kg / s,大4个数量级。质子的估计直径远远大于(估计为1.6e-15米)。音频数据的最小非零值约为2.3e-10。您不太可能看到浮点值是真实的任何小于2.35e-38但零的合法测量值。

如果高位字节为0xFF则转向另一个方向,则该值为无限,NaN或大于3.4e + 38的大小。宇宙的年龄估计为1.3e + 10年(1.3e + 25飞秒)。可观察的宇宙大约有e + 23颗星,Avagadro的数字是6.02e + 23。再次浮动值大于e + 38很少出现在合法测量中。

这并不是说FPU无法加载或生成这样的值,如果您正在使用现代FPU,您肯定会在计算的中间值中看到它们。现代FPU将加载浮点值,其指数为0但其他位不为0.这些值称为非规范化值。这就是为什么你看到小的正整数显示为e-42范围内的浮点值,即使浮点数的正常范围仅下降到e-38

所有1的指数代表无穷大。您可能不会在数据中找到无穷大,但您会比I更好。-Infinity是0xFF800000,+ Infinity是0x7F800000,Infinity的尾数中除0之外的任何值都是格式错误的。畸形无穷大被用作NaNs。

将NaN加载到float寄存器中会导致它抛出异常,所以你要使用整数数学来猜测你的数据是浮点数还是int数,直到你确定它是int为止。

答案 6 :(得分:0)

如果您知道您的浮点数都是实际值(没有NaN,INF,非正规值或其他异常值),那么您可以使用此标准。通常,一个int数组很可能包含“坏”浮点值。

答案 7 :(得分:0)

我假设以下内容:

  • 您的意思是IEEE 754单精度浮点数。
  • 浮点的符号位保存在int的MSB中。

所以我们走了:

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

答案 8 :(得分:0)

简化Alan所说的,我只看整数形式。并说,如果数字大于99999999那么它几乎肯定是一个浮点数。

这样做的好处是快速,简单,并避免了纳米问题。

它有一个缺点,它几乎充满了废话...我实际上并没有看到这些代表或任何东西的浮动,但从你的例子看起来合理......

无论如何,这是一种启发式方法,所以它的GONNA充满了垃圾,而且总是无论如何......

用千分尺测量,用粉笔标记,用斧头切割。

答案 9 :(得分:0)

根据@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's 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));
}