请教:如何使用JavaScript读取二进制文件内的“半精度Float”(也叫作half-precision float或Float16)数值?
发布于 8 年前 作者 stuartZhang 5214 次浏览 来自 问答

路过的大侠们救救我呀。 我正在使用 jDataView + jBinary 的库组合 拆包/解析 二进制矢量图型文件(类似于MVT格式,就是压缩率比MVT还高)。我遇到的问题是,图形矢量文件内保存的坐标信息都以“半精度Float”(也叫作half-precision float或Float16)类型存储的。 我目前使用jBinary的read(‘uint16’)的API把16bit的值给读了出来(这样可以确保后面的数据能够被位对齐地继续正确地读取),但是,这个读出来是“整型数”,不是我想要的Float16。

我想请教,我接下来,如何对那个被读出来的临时的 uint16数值 进行 位操作 转换出 我需要的 “Float16”?

3 回复

写了个转换的方法 https://en.wikipedia.org/wiki/Half-precision_floating-point_format

var float16 = function (n) {
    n = n & 0xffff;

    var sign = n >> 15;
    sign = sign ? -1 : 1;
    var exponent = (n >> 10) & 0x1f;
    var fraction = n & 0x3ff;

    var fr = 1;
    if (exponent == 0x1f) {
        return fraction == 0 ? sign * Infinity : NaN;
    } else if (exponent == 0) {
        fr = 0;
        exponent = -14;
    } else {
        exponent = exponent - 0xf;
    }


    for (var i = 0; i < 10; i++) {
        fr += ((fraction >> (10 - i -1)) & 0x1) * Math.pow(1/2, i + 1);
    }

    return sign * fr * Math.pow(2, exponent);
};

console.log(float16(0x3c00), 1);
console.log(float16(0x3c01), 1.0009765625);
console.log(float16(0xc000), -2);
console.log(float16(0x7bff), 65504);
console.log(float16(0x0400), '6.10352 × 10−5');
console.log(float16(0x03ff), '6.09756 × 10−5');
console.log(float16(0x0001), '5.96046 × 10−8');
console.log(float16(0x0000), '0');
console.log(float16(0x8000), '-0');
console.log(float16(0x7c00), Infinity);
console.log(float16(0xfc00), -Infinity);
console.log(float16(0x3555), 0.333251953125);

本以为是一个简单的事,其实不是:

var cmsb = function(n) {
    var e = 0;
    if (n > 0xff) {n >>= 8; e += 8;}
    if (n > 0xf)  {n >>= 4; e += 4;}
    if (n > 0x3)  {n >>= 2; e += 2;}
    e += n >> 1;
    return e;
}

var float16 = function (n) {
    n = n & 0xffff;
    var sign = n >> 15 & 0x1
    var exp = n >> 10 & 0x1f;
    var faction = n & 0x3ff;
    if (exp == 0) {
        if (faction > 0) {
            var m = 10 - cmsb(faction);
            faction = faction << m & 0x3ff;
            exp = 113 - m;
        }
    } else if (exp == 31) {
        exp = 255;
    } else {
        exp += 127 - 15;
    }
    console.log("=>", sign, exp, faction)

    var buffer = new ArrayBuffer(4);
    var ints = new Uint32Array(buffer);
    ints[0] = sign << 31 | exp << 23 | faction << 13;
    return new Float32Array(buffer)[0];
};
console.log(float16(0x7c80), NaN);
console.log(float16(0x3c00), 1);
console.log(float16(0x3c01), 1.0009765625);
console.log(float16(0xc000), -2);
console.log(float16(0x7bff), 65504);
console.log(float16(0x0400), '6.10352E-5');
console.log(float16(0x03ff), '6.09756E-5');
console.log(float16(0x0001), '5.96046E-8');
console.log(float16(0x0000), '0');
console.log(float16(0x8000), '-0');
console.log(float16(0x7c00), Infinity);
console.log(float16(0xfc00), -Infinity);
console.log(float16(0x3555), 0.333251953125);

@hl4 谢谢大神呀。在我同一个项目里,又遇到了另一个棘手的问题:https://cnodejs.org/topic/585f421b708f21aa5db0eccf 。大神能不能也提供我一些线索呀?

回到顶部