什么是最好的启发式方法,我可以用来确定一个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 ++,但您可以使用任何语言,伪代码或英语进行回答。
答案 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)
我假设以下内容:
所以我们走了:
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));
}