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