Passed
Push — master ( db344a...14761d )
by Rafael S.
01:56
created

IEEE754.js ➔ unpack   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 24
c 0
b 0
f 0
nc 8
nop 4
dl 0
loc 35
rs 8.3706
1
/*
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 * Copyright (c) 2013 DeNA Co., Ltd.
4
 * Copyright (c) 2010, Linden Research, Inc
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining
7
 * a copy of this software and associated documentation files (the
8
 * "Software"), to deal in the Software without restriction, including
9
 * without limitation the rights to use, copy, modify, merge, publish,
10
 * distribute, sublicense, and/or sell copies of the Software, and to
11
 * permit persons to whom the Software is furnished to do so, subject to
12
 * the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be
15
 * included in all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
 *
25
 */
26
27
/**
28
 * @fileoverview Functions to pack and unpack IEEE 754 floating point numbers.
29
 * @see https://github.com/rochars/byte-data
30
 */
31
32
/**
33
 * Pack a IEEE754 floating point number.
34
 * Derived from typedarray.js by Linden Research, MIT License.
35
 * Adapted to round overflows and underflows to Infinity and -Infinity.
36
 * @see https://bitbucket.org/lindenlab/llsd/raw/7d2646cd3f9b4c806e73aebc4b32bd81e4047fdc/js/typedarray.js
37
 * @param {!Uint8Array|!Array<number>} buffer The buffer.
38
 * @param {number} index The index to write on the buffer.
39
 * @param {number} num The number.
40
 * @param {number} ebits The number of bits of the exponent.
41
 * @param {number} fbits The number of bits of the fraction.
42
 * @return {number} The next index to write on the buffer.
43
 */
44
export function pack(buffer, index, num, ebits, fbits) {
45
  /** @type {number} */
46
  let bias = (1 << (ebits - 1)) - 1;
47
  // Round overflows and underflows
48
  if (Math.abs(num) > Math.pow(2, bias + 1) - ((ebits + fbits) * 2)) {
49
    num = num < 0 ? -Infinity : Infinity;
50
  }
51
  /**
52
   * sign, need this to handle negative zero
53
   * @see http://cwestblog.com/2014/02/25/javascript-testing-for-negative-zero/
54
   * @type {number}
55
   */
56
  let sign = (((num = +num) || 1 / num) < 0) ? 1 : num < 0 ? 1 : 0;
57
  num = Math.abs(num);
58
  /** @type {number} */
59
  let exp = Math.min(Math.floor(Math.log(num) / Math.LN2), 1023);
60
  /** @type {number} */
61
  let fraction = Math.round(num / Math.pow(2, exp) * Math.pow(2, fbits));
62
  // NaN
63
  if (num !== num) {
64
    fraction = Math.pow(2, fbits - 1);
65
    exp = (1 << ebits) - 1;
66
  // Number
67
  } else if (num !== 0) {
68
    if (num >= Math.pow(2, 1 - bias)) {
69
      if (fraction / Math.pow(2, fbits) >= 2) {
70
        exp = exp + 1;
71
        fraction = 1;
72
      }
73
      // Overflow
74
      if (exp > bias) {
75
        exp = (1 << ebits) - 1;
76
        fraction = 0;
77
      } else {
78
        exp = exp + bias;
79
        fraction = fraction - Math.pow(2, fbits);
80
      }
81
    } else {
82
      fraction = Math.round(num / Math.pow(2, 1 - bias - fbits));
83
      exp = 0;
84
    } 
85
  }
86
  return packFloatBits_(buffer, index, ebits, fbits, sign, exp, fraction);
87
}
88
89
/**
90
 * Unpack a IEEE754 floating point number.
91
 * Derived from IEEE754 by DeNA Co., Ltd., MIT License. 
92
 * Adapted to handle NaN. Should port the solution to the original repo.
93
 * @see https://github.com/kazuho/ieee754.js/blob/master/ieee754.js
94
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer to unpack.
95
 * @param {number} index the start index to read.
96
 * @param {number} ebits The number of bits of the exponent.
97
 * @param {number} fbits The number of bits of the fraction.
98
 */
99
export function unpack(buffer, index, ebits, fbits) {
100
  let exponentBias = (1 << (ebits - 1)) - 1;
101
  let numBytes = Math.ceil((ebits + fbits) / 8);
102
  /** @type {number} */
103
  let eMax = (1 << ebits) - 1;
104
  /** @type {number} */
105
  let bias = Math.pow(2, -(8 * numBytes - 1 - ebits));
106
  /** @type {number} */
107
  let significand;
108
  /** @type {string} */
109
  let leftBits = "";
110
  for (let i = numBytes - 1; i >= 0 ; i--) {
111
    /** @type {string} */
112
    let t = buffer[i + index].toString(2);
113
    leftBits += "00000000".substring(t.length) + t;
114
  }
115
  /** @type {number} */
116
  let sign = leftBits.charAt(0) == "1" ? -1 : 1;
117
  leftBits = leftBits.substring(1);
118
  /** @type {number} */
119
  let exponent = parseInt(leftBits.substring(0, ebits), 2);
120
  leftBits = leftBits.substring(ebits);
121
  if (exponent == eMax) {
122
    if (parseInt(leftBits, 2) !== 0) {
123
      return NaN;
124
    }
125
    return sign * Infinity;  
126
  } else if (exponent == 0) {
0 ignored issues
show
Best Practice introduced by Rafael Rocha
Comparing exponent to 0 using the == operator is not safe. Consider using === instead.
Loading history...
127
    exponent += 1;
128
    significand = parseInt(leftBits, 2);
129
  } else {
130
    significand = parseInt("1" + leftBits, 2);
131
  }
132
  return sign * significand * bias * Math.pow(2, exponent - exponentBias);
133
}
134
135
/**
136
 * Pack a IEEE754 from its sign, exponent and fraction bits
137
 * and place it in a byte buffer.
138
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer to write to.
139
 * @param {number} index The buffer index to write.
140
 * @param {number} ebits The number of bits of the exponent.
141
 * @param {number} fbits The number of bits of the fraction.
142
 * @param {number} sign The sign.
143
 * @param {number} exp the exponent.
144
 * @param {number} fraction The fraction.
145
 * @return {number}
146
 * @private
147
 */
148
function packFloatBits_(buffer, index, ebits, fbits, sign, exp, fraction) {
149
  /** @type {!Array<number>} */
150
  let bits = [];
151
  // the signal
152
  bits.push(sign);
153
  // the exponent
154
  for (let i = ebits; i > 0; i -= 1) {
155
    bits[i] = (exp % 2 ? 1 : 0);
156
    exp = Math.floor(exp / 2);
157
  }
158
  // the fraction
159
  for (let i = fbits, len = bits.length; i > 0; i -= 1) {
160
    bits[len + i] = (fraction % 2 ? 1 : 0);
161
    fraction = Math.floor(fraction / 2);
162
  }
163
  // pack as bytes
164
  /** @type {string} */
165
  let str = bits.join('');
166
  /** @type {number} */
167
  let numBytes = Math.ceil((ebits + fbits + 1) / 8) + index - 1; // FIXME
168
  numBytes = numBytes == 2 ? 1 : numBytes; // FIXME
169
  /** @type {number} */
170
  let k = index;
171
  while (numBytes >= index) {
172
    buffer[numBytes] = parseInt(str.substring(0, 8), 2);
173
    str = str.substring(8);
174
    numBytes--;
175
    k++;
176
  }
177
  return k;
178
}
179