Passed
Push — master ( 5ff06a...d1d19e )
by Rafael S.
01:41
created

floats.js ➔ toHalf   A

Complexity

Conditions 2
Paths 5

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 13
rs 9.4285
1
/*
2
 * float: Functions to work with 16, 32 & 64 bit floats.
3
 * Copyright (c) 2017 Rafael da Silva Rocha.
4
 * https://github.com/rochars/byte-data
5
 */
6
7
const helpers = require('../src/helpers');
8
9
function getBinary(bytes, rev=false) {
10
    let binary = "";
11
    let i = 0;
12
    let bytesLength = bytes.length;
13
    while(i < bytesLength) {
14
        let bits = helpers.lPadZeros(bytes[i].toString(2), 8);
15
        if (rev) {
16
            binary = binary + bits;
17
        } else {
18
            binary = bits + binary;
19
        }
20
        i++;
21
    }
22
    return binary;
23
}
24
25
/**
26
 * Turn bytes to a float 16..
27
 * Thanks https://stackoverflow.com/a/8796597
28
 * @param {number} bytes 2 bytes representing a float 16.
29
 */
30
function decodeFloat16 (bytes) {
31
    let binary = parseInt(getBinary(bytes, true), 2);
32
    let exponent = (binary & 0x7C00) >> 10;
33
    let fraction = binary & 0x03FF;
34
    let floatValue;
35
    if (exponent) {
36
        floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
37
    } else {
38
        floatValue = 6.103515625e-5 * (fraction / 0x400);
39
    }
40
    return  floatValue * (binary >> 15 ? -1 : 1);
41
}
42
43
/**
44
 * Turn an array of bytes into a float 64.
45
 * Thanks https://gist.github.com/kg/2192799
46
 * @param {!Array<number>} bytes 8 bytes representing a float 64.
47
 */
48
function decodeFloat64(bytes) {
49
    if (bytes.toString() == "0,0,0,0,0,0,0,0") {
50
        return 0;
51
    }
52
    let binary = getBinary(bytes);
53
    let significandBin = "1" + binary.substr(1 + 11, 52);
54
    let val = 1;
55
    let significand = 0;
56
    let i = 0;
57
    while (i < significandBin.length) {
58
        significand += val * parseInt(significandBin.charAt(i), 10);
59
        val = val / 2;
60
        i++;
61
    }
62
    let sign = (binary.charAt(0) == "1") ? -1 : 1;
63
    let doubleValue = sign * significand *
64
        Math.pow(2, parseInt(binary.substr(1, 11), 2) - 1023);
65
    return doubleValue;
66
}
67
68
/**
69
 * Unpack a 64 bit float into two words.
70
 * Thanks https://stackoverflow.com/a/16043259
71
 * @param {number} value A float64 number.
72
 */
73
function toFloat64(value) {
74
    if (value == 0) {
75
        return [0, 0];
76
    }
77
    let hiWord = 0;
78
    let loWord = 0;
79
    if (value <= 0.0) {
80
        hiWord = 0x80000000;
81
        value = -value;
82
    }
83
    let exponent = Math.floor(
84
        Math.log(value) / Math.log(2));
85
    let significand = Math.floor(
86
        (value / Math.pow(2, exponent)) * Math.pow(2, 52));
87
    loWord = significand & 0xFFFFFFFF;
88
    significand /= Math.pow(2, 32);
89
    exponent += 1023;
90
    hiWord = hiWord | (exponent << 20);
91
    hiWord = hiWord | (significand & ~(-1 << 20));
92
    return [hiWord, loWord];
93
}
94
95
let floatView = new Float32Array(1);
96
let int32View = new Int32Array(floatView.buffer);
97
98
/**
99
 * to-half: int bits of half-precision floating point values
100
 * Based on:
101
 * https://mail.mozilla.org/pipermail/es-discuss/2017-April/047994.html
102
 * https://github.com/rochars/byte-data
103
 */
104
function toHalf(val) {
105
    floatView[0] = val;
106
    let x = int32View[0];
107
    let bits = (x >> 16) & 0x8000;
108
    let m = (x >> 12) & 0x07ff;
109
    let e = (x >> 23) & 0xff;
110
    if (e < 103) {
111
        return bits;
112
    }
113
    bits |= ((e - 112) << 10) | (m >> 1);
114
    bits += m & 1;
115
    return bits;
116
}
117
118
module.exports.getBinary = getBinary;
119
module.exports.decodeFloat16 = decodeFloat16;
120
module.exports.decodeFloat64 = decodeFloat64;
121
module.exports.toFloat64 = toFloat64;
122
module.exports.toHalf = toHalf;
123