Passed
Push — master ( 2f7ce0...4f126f )
by Rafael S.
02:01
created

IEEE754.js ➔ roundToEven   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 8
rs 10
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
function roundToEven(n) {
33
      var w = Math.floor(n), f = n - w;
34
      if (f < 0.5)
35
        return w;
0 ignored issues
show
Coding Style Best Practice introduced by Rafael Rocha
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
36
      if (f > 0.5)
37
        return w + 1;
0 ignored issues
show
Coding Style Best Practice introduced by Rafael Rocha
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
38
      return w % 2 ? w + 1 : w;
39
}
40
41
/**
42
 * Pack a IEEE754 floating point number.
43
 * Derived from typedarray.js by Linden Research, MIT License.
44
 * Adapted to round overflows to Infinity and -Infinity.
45
 * @see https://bitbucket.org/lindenlab/llsd/raw/7d2646cd3f9b4c806e73aebc4b32bd81e4047fdc/js/typedarray.js
46
 * @param {!Uint8Array|!Array<number>} buffer The buffer.
47
 * @param {number} index The index to write on the buffer.
48
 * @param {number} num The number.
49
 * @param {number} ebits The number of bits of the exponent.
50
 * @param {number} fbits The number of bits of the fraction.
51
 * @return {number} The next index to write on the buffer.
52
 */
53
export function pack(buffer, index, num, ebits, fbits) {
54
  /** @type {number} */
55
  let bias = (1 << (ebits - 1)) - 1;
56
  // Round overflows
57
  if (Math.abs(num) > Math.pow(2, bias + 1) - ((ebits + fbits) * 2)) {
58
    num = num < 0 ? -Infinity : Infinity;
59
  }
60
  /**
61
   * sign, need this to handle negative zero
62
   * @see http://cwestblog.com/2014/02/25/javascript-testing-for-negative-zero/
63
   * @type {number}
64
   */
65
  let sign = (((num = +num) || 1 / num) < 0) ? 1 : num < 0 ? 1 : 0;
66
  num = Math.abs(num);
67
  /** @type {number} */
68
  let exp = Math.min(Math.floor(Math.log(num) / Math.LN2), 1023);
69
  /** @type {number} */
70
  let fraction = roundToEven(num / Math.pow(2, exp) * Math.pow(2, fbits));
71
  // NaN
72
  if (num !== num) {
73
    fraction = Math.pow(2, fbits - 1);
74
    exp = (1 << ebits) - 1;
75
  // Number
76
  } else if (num !== 0) {
77
    if (num >= Math.pow(2, 1 - bias)) {
78
      if (fraction / Math.pow(2, fbits) >= 2) {
79
        exp = exp + 1;
80
        fraction = 1;
81
      }
82
      // Overflow
83
      if (exp > bias) {
84
        exp = roundToEven((1 << ebits)) - 1;
85
        fraction = 0;
86
      } else {
87
        exp = exp + bias;
88
        fraction = roundToEven(fraction) - Math.pow(2, fbits);
89
      }
90
    } else {
91
      //fraction = Math.round(num / Math.pow(2, 1 - bias - fbits));
92
      fraction = roundToEven(num / Math.pow(2, 1 - bias - fbits));
93
      exp = 0;
94
    } 
95
  }
96
  return packFloatBits_(buffer, index, ebits, fbits, sign, exp, fraction);
97
}
98
99
/**
100
 * Unpack a IEEE754 floating point number.
101
 * Derived from IEEE754 by DeNA Co., Ltd., MIT License. 
102
 * Adapted to handle NaN. Should port the solution to the original repo.
103
 * @see https://github.com/kazuho/ieee754.js/blob/master/ieee754.js
104
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer to unpack.
105
 * @param {number} index the start index to read.
106
 * @param {number} ebits The number of bits of the exponent.
107
 * @param {number} fbits The number of bits of the fraction.
108
 */
109
export function unpack(buffer, index, ebits, fbits) {
110
  let exponentBias = (1 << (ebits - 1)) - 1;
111
  let numBytes = Math.ceil((ebits + fbits) / 8);
112
  /** @type {number} */
113
  let eMax = (1 << ebits) - 1;
114
  /** @type {number} */
115
  let bias = Math.pow(2, -(8 * numBytes - 1 - ebits));
116
  /** @type {number} */
117
  let significand;
118
  /** @type {string} */
119
  let leftBits = "";
120
  for (let i = numBytes - 1; i >= 0 ; i--) {
121
    /** @type {string} */
122
    let t = buffer[i + index].toString(2);
123
    leftBits += "00000000".substring(t.length) + t;
124
  }
125
  /** @type {number} */
126
  let sign = leftBits.charAt(0) == "1" ? -1 : 1;
127
  leftBits = leftBits.substring(1);
128
  /** @type {number} */
129
  let exponent = parseInt(leftBits.substring(0, ebits), 2);
130
  leftBits = leftBits.substring(ebits);
131
  if (exponent == eMax) {
132
    if (parseInt(leftBits, 2) !== 0) {
133
      return NaN;
134
    }
135
    return sign * Infinity;  
136
  } 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...
137
    exponent += 1;
138
    significand = parseInt(leftBits, 2);
139
  } else {
140
    significand = parseInt("1" + leftBits, 2);
141
  }
142
  return sign * significand * bias * Math.pow(2, exponent - exponentBias);
143
}
144
145
/**
146
 * Pack a IEEE754 from its sign, exponent and fraction bits
147
 * and place it in a byte buffer.
148
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer to write to.
149
 * @param {number} index The buffer index to write.
150
 * @param {number} ebits The number of bits of the exponent.
151
 * @param {number} fbits The number of bits of the fraction.
152
 * @param {number} sign The sign.
153
 * @param {number} exp the exponent.
154
 * @param {number} fraction The fraction.
155
 * @return {number}
156
 * @private
157
 */
158
function packFloatBits_(buffer, index, ebits, fbits, sign, exp, fraction) {
159
  /** @type {!Array<number>} */
160
  let bits = [];
161
  // the signal
162
  bits.push(sign);
163
  // the exponent
164
  for (let i = ebits; i > 0; i -= 1) {
165
    bits[i] = (exp % 2 ? 1 : 0);
166
    exp = Math.floor(exp / 2);
167
  }
168
  // the fraction
169
  let len = bits.length;
170
  for (let i = fbits; i > 0; i -= 1) {
171
    bits[len + i] = (fraction % 2 ? 1 : 0);
172
    fraction = Math.floor(fraction / 2);
173
  }
174
  // pack as bytes
175
  /** @type {string} */
176
  let str = bits.join('');
177
  /** @type {number} */
178
  let numBytes = Math.ceil((ebits + fbits + 1) / 8) + index - 1; // FIXME
179
  numBytes = numBytes == 2 ? 1 : numBytes; // FIXME
180
  /** @type {number} */
181
  let k = index;
182
  while (numBytes >= index) {
183
    buffer[numBytes] = parseInt(str.substring(0, 8), 2);
184
    str = str.substring(8);
185
    numBytes--;
186
    k++;
187
  }
188
  return k;
189
}
190