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
|
|||
36 | if (f > 0.5) |
||
37 | return w + 1; |
||
0 ignored issues
–
show
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 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) { |
||
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 |
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 you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42
will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.