Total Complexity | 117 |
Complexity/F | 11.7 |
Lines of Code | 728 |
Function Count | 10 |
Duplicated Lines | 641 |
Ratio | 88.05 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like formio/node_modules/bson/lib/bson/decimal128.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | "use strict" |
||
2 | |||
3 | var Long = require('./long'); |
||
4 | |||
5 | var PARSE_STRING_REGEXP = /^(\+|\-)?(\d+|(\d*\.\d*))?(E|e)?([\-\+])?(\d+)?$/; |
||
6 | var PARSE_INF_REGEXP = /^(\+|\-)?(Infinity|inf)$/i; |
||
7 | var PARSE_NAN_REGEXP = /^(\+|\-)?NaN$/i; |
||
8 | |||
9 | var EXPONENT_MAX = 6111; |
||
10 | var EXPONENT_MIN = -6176; |
||
11 | var EXPONENT_BIAS = 6176; |
||
12 | var MAX_DIGITS = 34; |
||
13 | |||
14 | // Nan value bits as 32 bit values (due to lack of longs) |
||
15 | var NAN_BUFFER = [0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].reverse(); |
||
16 | // Infinity value bits 32 bit values (due to lack of longs) |
||
17 | var INF_NEGATIVE_BUFFER = [0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].reverse(); |
||
18 | var INF_POSITIVE_BUFFER = [0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].reverse(); |
||
19 | |||
20 | var EXPONENT_REGEX = /^([\-\+])?(\d+)?$/; |
||
21 | |||
22 | |||
23 | // Detect if the value is a digit |
||
24 | var isDigit = function(value) { |
||
25 | return !isNaN(parseInt(value, 10)); |
||
26 | } |
||
27 | |||
28 | // Divide two uint128 values |
||
29 | View Code Duplication | var divideu128 = function(value) { |
|
30 | var DIVISOR = Long.fromNumber(1000 * 1000 * 1000); |
||
31 | var _rem = Long.fromNumber(0); |
||
32 | var i = 0; |
||
33 | |||
34 | if(!value.parts[0] && !value.parts[1] && |
||
35 | !value.parts[2] && !value.parts[3]) { |
||
36 | return { quotient: value, rem: _rem }; |
||
37 | } |
||
38 | |||
39 | for(var i = 0; i <= 3; i++) { |
||
40 | // Adjust remainder to match value of next dividend |
||
41 | _rem = _rem.shiftLeft(32); |
||
42 | // Add the divided to _rem |
||
43 | _rem = _rem.add(new Long(value.parts[i], 0)); |
||
44 | value.parts[i] = _rem.div(DIVISOR).low_; |
||
45 | _rem = _rem.modulo(DIVISOR); |
||
46 | } |
||
47 | |||
48 | return { quotient: value, rem: _rem }; |
||
49 | } |
||
50 | |||
51 | // Multiply two Long values and return the 128 bit value |
||
52 | View Code Duplication | var multiply64x2 = function(left, right) { |
|
53 | if(!left && !right) { |
||
54 | return {high: Long.fromNumber(0), low: Long.fromNumber(0)}; |
||
55 | } |
||
56 | |||
57 | var leftHigh = left.shiftRightUnsigned(32); |
||
58 | var leftLow = new Long(left.getLowBits(), 0); |
||
59 | var rightHigh = right.shiftRightUnsigned(32); |
||
60 | var rightLow = new Long(right.getLowBits(), 0); |
||
61 | |||
62 | var productHigh = leftHigh.multiply(rightHigh); |
||
63 | var productMid = leftHigh.multiply(rightLow); |
||
64 | var productMid2 = leftLow.multiply(rightHigh); |
||
65 | var productLow = leftLow.multiply(rightLow); |
||
66 | |||
67 | productHigh = productHigh.add(productMid.shiftRightUnsigned(32)); |
||
68 | productMid = new Long(productMid.getLowBits(), 0) |
||
69 | .add(productMid2) |
||
70 | .add(productLow.shiftRightUnsigned(32)); |
||
71 | |||
72 | productHigh = productHigh.add(productMid.shiftRightUnsigned(32)); |
||
73 | productLow = productMid.shiftLeft(32).add(new Long(productLow.getLowBits(), 0)); |
||
74 | |||
75 | // Return the 128 bit result |
||
76 | return {high: productHigh, low: productLow}; |
||
77 | } |
||
78 | |||
79 | var lessThan = function(left, right) { |
||
80 | // Make values unsigned |
||
81 | var uhleft = left.high_ >>> 0; |
||
82 | var uhright = right.high_ >>> 0; |
||
83 | |||
84 | // Compare high bits first |
||
85 | if(uhleft < uhright) { |
||
86 | return true |
||
87 | } else if(uhleft == uhright) { |
||
88 | var ulleft = left.low_ >>> 0; |
||
89 | var ulright = right.low_ >>> 0; |
||
90 | if(ulleft < ulright) return true; |
||
91 | } |
||
92 | |||
93 | return false; |
||
94 | } |
||
95 | |||
96 | View Code Duplication | var longtoHex = function(value) { |
|
97 | var buffer = new Buffer(8); |
||
98 | var index = 0; |
||
99 | // Encode the low 64 bits of the decimal |
||
100 | // Encode low bits |
||
101 | buffer[index++] = value.low_ & 0xff; |
||
102 | buffer[index++] = (value.low_ >> 8) & 0xff; |
||
103 | buffer[index++] = (value.low_ >> 16) & 0xff; |
||
104 | buffer[index++] = (value.low_ >> 24) & 0xff; |
||
105 | // Encode high bits |
||
106 | buffer[index++] = value.high_ & 0xff; |
||
107 | buffer[index++] = (value.high_ >> 8) & 0xff; |
||
108 | buffer[index++] = (value.high_ >> 16) & 0xff; |
||
109 | buffer[index++] = (value.high_ >> 24) & 0xff; |
||
|
|||
110 | return buffer.reverse().toString('hex'); |
||
111 | } |
||
112 | |||
113 | var int32toHex = function(value) { |
||
114 | var buffer = new Buffer(4); |
||
115 | var index = 0; |
||
116 | // Encode the low 64 bits of the decimal |
||
117 | // Encode low bits |
||
118 | buffer[index++] = value & 0xff; |
||
119 | buffer[index++] = (value >> 8) & 0xff; |
||
120 | buffer[index++] = (value >> 16) & 0xff; |
||
121 | buffer[index++] = (value >> 24) & 0xff; |
||
122 | return buffer.reverse().toString('hex'); |
||
123 | } |
||
124 | |||
125 | var Decimal128 = function(bytes) { |
||
126 | this._bsontype = 'Decimal128'; |
||
127 | this.bytes = bytes; |
||
128 | } |
||
129 | |||
130 | View Code Duplication | Decimal128.fromString = function(string) { |
|
131 | // Parse state tracking |
||
132 | var isNegative = false; |
||
133 | var sawRadix = false; |
||
134 | var foundNonZero = false; |
||
135 | |||
136 | // Total number of significant digits (no leading or trailing zero) |
||
137 | var significantDigits = 0; |
||
138 | // Total number of significand digits read |
||
139 | var nDigitsRead = 0; |
||
140 | // Total number of digits (no leading zeros) |
||
141 | var nDigits = 0; |
||
142 | // The number of the digits after radix |
||
143 | var radixPosition = 0; |
||
144 | // The index of the first non-zero in *str* |
||
145 | var firstNonZero = 0; |
||
146 | |||
147 | // Digits Array |
||
148 | var digits = [0]; |
||
149 | // The number of digits in digits |
||
150 | var nDigitsStored = 0; |
||
151 | // Insertion pointer for digits |
||
152 | var digitsInsert = 0; |
||
153 | // The index of the first non-zero digit |
||
154 | var firstDigit = 0; |
||
155 | // The index of the last digit |
||
156 | var lastDigit = 0; |
||
157 | |||
158 | // Exponent |
||
159 | var exponent = 0; |
||
160 | // loop index over array |
||
161 | var i = 0; |
||
162 | // The high 17 digits of the significand |
||
163 | var significandHigh = [0, 0]; |
||
164 | // The low 17 digits of the significand |
||
165 | var significandLow = [0, 0]; |
||
166 | // The biased exponent |
||
167 | var biasedExponent = 0; |
||
168 | |||
169 | // Read index |
||
170 | var index = 0; |
||
171 | |||
172 | // Trim the string |
||
173 | string = string.trim(); |
||
174 | |||
175 | // Results |
||
176 | var stringMatch = string.match(PARSE_STRING_REGEXP); |
||
177 | var infMatch = string.match(PARSE_INF_REGEXP); |
||
178 | var nanMatch = string.match(PARSE_NAN_REGEXP); |
||
179 | |||
180 | // Validate the string |
||
181 | if(!stringMatch |
||
182 | && ! infMatch |
||
183 | && ! nanMatch || string.length == 0) { |
||
184 | throw new Error("" + string + " not a valid Decimal128 string"); |
||
185 | } |
||
186 | |||
187 | // Check if we have an illegal exponent format |
||
188 | if(stringMatch && stringMatch[4] && stringMatch[2] === undefined) { |
||
189 | throw new Error("" + string + " not a valid Decimal128 string"); |
||
190 | } |
||
191 | |||
192 | // Get the negative or positive sign |
||
193 | if(string[index] == '+' || string[index] == '-') { |
||
194 | isNegative = string[index++] == '-'; |
||
195 | } |
||
196 | |||
197 | // Check if user passed Infinity or NaN |
||
198 | if(!isDigit(string[index]) && string[index] != '.') { |
||
199 | if(string[index] == 'i' || string[index] == 'I') { |
||
200 | return new Decimal128(new Buffer(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)); |
||
201 | } else if(string[index] == 'N') { |
||
202 | return new Decimal128(new Buffer(NAN_BUFFER)); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | // Read all the digits |
||
207 | while(isDigit(string[index]) || string[index] == '.') { |
||
208 | if(string[index] == '.') { |
||
209 | if(sawRadix) { |
||
210 | return new Decimal128(new Buffer(NAN_BUFFER)); |
||
211 | } |
||
212 | |||
213 | sawRadix = true; |
||
214 | index = index + 1; |
||
215 | continue; |
||
216 | } |
||
217 | |||
218 | if(nDigitsStored < 34) { |
||
219 | if(string[index] != '0' || foundNonZero) { |
||
220 | if(!foundNonZero) { |
||
221 | firstNonZero = nDigitsRead; |
||
222 | } |
||
223 | |||
224 | foundNonZero = true; |
||
225 | |||
226 | // Only store 34 digits |
||
227 | digits[digitsInsert++] = parseInt(string[index], 10); |
||
228 | nDigitsStored = nDigitsStored + 1; |
||
229 | } |
||
230 | } |
||
231 | |||
232 | if(foundNonZero) { |
||
233 | nDigits = nDigits + 1; |
||
234 | } |
||
235 | |||
236 | if(sawRadix) { |
||
237 | radixPosition = radixPosition + 1; |
||
238 | } |
||
239 | |||
240 | nDigitsRead = nDigitsRead + 1; |
||
241 | index = index + 1; |
||
242 | } |
||
243 | |||
244 | if(sawRadix && !nDigitsRead) { |
||
245 | throw new Error("" + string + " not a valid Decimal128 string"); |
||
246 | } |
||
247 | |||
248 | // Read exponent if exists |
||
249 | if(string[index] == 'e' || string[index] == 'E') { |
||
250 | // Read exponent digits |
||
251 | var match = string.substr(++index).match(EXPONENT_REGEX); |
||
252 | |||
253 | // No digits read |
||
254 | if(!match || !match[2]) { |
||
255 | return new Decimal128(new Buffer(NAN_BUFFER)); |
||
256 | } |
||
257 | |||
258 | // Get exponent |
||
259 | exponent = parseInt(match[0], 10); |
||
260 | |||
261 | // Adjust the index |
||
262 | index = index + match[0].length; |
||
263 | } |
||
264 | |||
265 | // Return not a number |
||
266 | if(string[index]) { |
||
267 | return new Decimal128(new Buffer(NAN_BUFFER)); |
||
268 | } |
||
269 | |||
270 | // Done reading input |
||
271 | // Find first non-zero digit in digits |
||
272 | firstDigit = 0; |
||
273 | |||
274 | if(!nDigitsStored) { |
||
275 | firstDigit = 0; |
||
276 | lastDigit = 0; |
||
277 | digits[0] = 0; |
||
278 | nDigits = 1; |
||
279 | nDigitsStored = 1; |
||
280 | significantDigits = 0; |
||
281 | } else { |
||
282 | lastDigit = nDigitsStored - 1; |
||
283 | significantDigits = nDigits; |
||
284 | |||
285 | if(exponent != 0 && significantDigits != 1) { |
||
286 | while(string[firstNonZero + significantDigits - 1] == '0') { |
||
287 | significantDigits = significantDigits - 1; |
||
288 | } |
||
289 | } |
||
290 | } |
||
291 | |||
292 | // Normalization of exponent |
||
293 | // Correct exponent based on radix position, and shift significand as needed |
||
294 | // to represent user input |
||
295 | |||
296 | // Overflow prevention |
||
297 | if(exponent <= radixPosition && radixPosition - exponent > (1 << 14)) { |
||
298 | exponent = EXPONENT_MIN; |
||
299 | } else { |
||
300 | exponent = exponent - radixPosition; |
||
301 | } |
||
302 | |||
303 | // Attempt to normalize the exponent |
||
304 | while(exponent > EXPONENT_MAX) { |
||
305 | // Shift exponent to significand and decrease |
||
306 | lastDigit = lastDigit + 1; |
||
307 | |||
308 | if(lastDigit - firstDigit > MAX_DIGITS) { |
||
309 | // Check if we have a zero then just hard clamp, otherwise fail |
||
310 | var digitsString = digits.join(''); |
||
311 | if(digitsString.match(/^0+$/)) { |
||
312 | exponent = EXPONENT_MAX; |
||
313 | break; |
||
314 | } else { |
||
315 | return new Decimal128(new Buffer(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)); |
||
316 | } |
||
317 | } |
||
318 | |||
319 | exponent = exponent - 1; |
||
320 | } |
||
321 | |||
322 | while(exponent < EXPONENT_MIN || nDigitsStored < nDigits) { |
||
323 | // Shift last digit |
||
324 | if(lastDigit == 0) { |
||
325 | exponent = EXPONENT_MIN; |
||
326 | significantDigits = 0; |
||
327 | break; |
||
328 | } |
||
329 | |||
330 | if(nDigitsStored < nDigits) { |
||
331 | // adjust to match digits not stored |
||
332 | nDigits = nDigits - 1; |
||
333 | } else { |
||
334 | // adjust to round |
||
335 | lastDigit = lastDigit - 1; |
||
336 | } |
||
337 | |||
338 | if(exponent < EXPONENT_MAX) { |
||
339 | exponent = exponent + 1; |
||
340 | } else { |
||
341 | // Check if we have a zero then just hard clamp, otherwise fail |
||
342 | var digitsString = digits.join(''); |
||
343 | if(digitsString.match(/^0+$/)) { |
||
344 | exponent = EXPONENT_MAX; |
||
345 | break; |
||
346 | } else { |
||
347 | return new Decimal128(new Buffer(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)) |
||
348 | } |
||
349 | } |
||
350 | } |
||
351 | |||
352 | |||
353 | // Round |
||
354 | // We've normalized the exponent, but might still need to round. |
||
355 | if((lastDigit - firstDigit + 1 < significantDigits) && string[significantDigits] != '0') { |
||
356 | var endOfString = nDigitsRead; |
||
357 | |||
358 | // If we have seen a radix point, 'string' is 1 longer than we have |
||
359 | // documented with ndigits_read, so inc the position of the first nonzero |
||
360 | // digit and the position that digits are read to. |
||
361 | if(sawRadix && exponent == EXPONENT_MIN) { |
||
362 | firstNonZero = firstNonZero + 1; |
||
363 | endOfString = endOfString + 1; |
||
364 | } |
||
365 | |||
366 | var roundDigit = parseInt(string[firstNonZero + lastDigit + 1], 10); |
||
367 | var roundBit = 0; |
||
368 | |||
369 | if(roundDigit >= 5) { |
||
370 | roundBit = 1; |
||
371 | |||
372 | if(roundDigit == 5) { |
||
373 | roundBit = digits[lastDigit] % 2 == 1; |
||
374 | |||
375 | for(var i = firstNonZero + lastDigit + 2; i < endOfString; i++) { |
||
376 | if(parseInt(string[i], 10)) { |
||
377 | roundBit = 1; |
||
378 | break; |
||
379 | } |
||
380 | } |
||
381 | } |
||
382 | } |
||
383 | |||
384 | if(roundBit) { |
||
385 | var dIdx = lastDigit; |
||
386 | |||
387 | for(; dIdx >= 0; dIdx--) { |
||
388 | if(++digits[dIdx] > 9) { |
||
389 | digits[dIdx] = 0; |
||
390 | |||
391 | // overflowed most significant digit |
||
392 | if(dIdx == 0) { |
||
393 | if(exponent < EXPONENT_MAX) { |
||
394 | exponent = exponent + 1; |
||
395 | digits[dIdx] = 1; |
||
396 | } else { |
||
397 | return new Decimal128(new Buffer(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER)) |
||
398 | } |
||
399 | } |
||
400 | } else { |
||
401 | break; |
||
402 | } |
||
403 | } |
||
404 | } |
||
405 | } |
||
406 | |||
407 | // Encode significand |
||
408 | // The high 17 digits of the significand |
||
409 | significandHigh = Long.fromNumber(0); |
||
410 | // The low 17 digits of the significand |
||
411 | significandLow = Long.fromNumber(0); |
||
412 | |||
413 | // read a zero |
||
414 | if(significantDigits == 0) { |
||
415 | significandHigh = Long.fromNumber(0); |
||
416 | significandLow = Long.fromNumber(0); |
||
417 | } else if(lastDigit - firstDigit < 17) { |
||
418 | var dIdx = firstDigit; |
||
419 | significandLow = Long.fromNumber(digits[dIdx++]); |
||
420 | significandHigh = new Long(0, 0); |
||
421 | |||
422 | for(; dIdx <= lastDigit; dIdx++) { |
||
423 | significandLow = significandLow.multiply(Long.fromNumber(10)); |
||
424 | significandLow = significandLow.add(Long.fromNumber(digits[dIdx])); |
||
425 | } |
||
426 | } else { |
||
427 | var dIdx = firstDigit; |
||
428 | significandHigh = Long.fromNumber(digits[dIdx++]); |
||
429 | |||
430 | for(; dIdx <= lastDigit - 17; dIdx++) { |
||
431 | significandHigh = significandHigh.multiply(Long.fromNumber(10)); |
||
432 | significandHigh = significandHigh.add(Long.fromNumber(digits[dIdx])); |
||
433 | } |
||
434 | |||
435 | significandLow = Long.fromNumber(digits[dIdx++]); |
||
436 | |||
437 | for(; dIdx <= lastDigit; dIdx++) { |
||
438 | significandLow = significandLow.multiply(Long.fromNumber(10)); |
||
439 | significandLow = significandLow.add(Long.fromNumber(digits[dIdx])); |
||
440 | } |
||
441 | } |
||
442 | |||
443 | var significand = multiply64x2(significandHigh, Long.fromString("100000000000000000")); |
||
444 | |||
445 | significand.low = significand.low.add(significandLow); |
||
446 | |||
447 | if(lessThan(significand.low, significandLow)) { |
||
448 | significand.high = significand.high.add(Long.fromNumber(1)); |
||
449 | } |
||
450 | |||
451 | // Biased exponent |
||
452 | var biasedExponent = (exponent + EXPONENT_BIAS); |
||
453 | var dec = { low: Long.fromNumber(0), high: Long.fromNumber(0) }; |
||
454 | |||
455 | // Encode combination, exponent, and significand. |
||
456 | if(significand.high.shiftRightUnsigned(49).and(Long.fromNumber(1)).equals(Long.fromNumber)) { |
||
457 | // Encode '11' into bits 1 to 3 |
||
458 | dec.high = dec.high.or(Long.fromNumber(0x3).shiftLeft(61)); |
||
459 | dec.high = dec.high.or(Long.fromNumber(biasedExponent).and(Long.fromNumber(0x3fff).shiftLeft(47))); |
||
460 | dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x7fffffffffff))); |
||
461 | } else { |
||
462 | dec.high = dec.high.or(Long.fromNumber(biasedExponent & 0x3fff).shiftLeft(49)); |
||
463 | dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x1ffffffffffff))); |
||
464 | } |
||
465 | |||
466 | dec.low = significand.low; |
||
467 | |||
468 | // Encode sign |
||
469 | if(isNegative) { |
||
470 | dec.high = dec.high.or(Long.fromString('9223372036854775808')); |
||
471 | } |
||
472 | |||
473 | // Encode into a buffer |
||
474 | var buffer = new Buffer(16); |
||
475 | var index = 0; |
||
476 | |||
477 | // Encode the low 64 bits of the decimal |
||
478 | // Encode low bits |
||
479 | buffer[index++] = dec.low.low_ & 0xff; |
||
480 | buffer[index++] = (dec.low.low_ >> 8) & 0xff; |
||
481 | buffer[index++] = (dec.low.low_ >> 16) & 0xff; |
||
482 | buffer[index++] = (dec.low.low_ >> 24) & 0xff; |
||
483 | // Encode high bits |
||
484 | buffer[index++] = dec.low.high_ & 0xff; |
||
485 | buffer[index++] = (dec.low.high_ >> 8) & 0xff; |
||
486 | buffer[index++] = (dec.low.high_ >> 16) & 0xff; |
||
487 | buffer[index++] = (dec.low.high_ >> 24) & 0xff; |
||
488 | |||
489 | // Encode the high 64 bits of the decimal |
||
490 | // Encode low bits |
||
491 | buffer[index++] = dec.high.low_ & 0xff; |
||
492 | buffer[index++] = (dec.high.low_ >> 8) & 0xff; |
||
493 | buffer[index++] = (dec.high.low_ >> 16) & 0xff; |
||
494 | buffer[index++] = (dec.high.low_ >> 24) & 0xff; |
||
495 | // Encode high bits |
||
496 | buffer[index++] = dec.high.high_ & 0xff; |
||
497 | buffer[index++] = (dec.high.high_ >> 8) & 0xff; |
||
498 | buffer[index++] = (dec.high.high_ >> 16) & 0xff; |
||
499 | buffer[index++] = (dec.high.high_ >> 24) & 0xff; |
||
500 | |||
501 | // Return the new Decimal128 |
||
502 | return new Decimal128(buffer); |
||
503 | } |
||
504 | |||
505 | // Extract least significant 5 bits |
||
506 | var COMBINATION_MASK = 0x1f; |
||
507 | // Extract least significant 14 bits |
||
508 | var EXPONENT_MASK = 0x3fff; |
||
509 | // Value of combination field for Inf |
||
510 | var COMBINATION_INFINITY = 30; |
||
511 | // Value of combination field for NaN |
||
512 | var COMBINATION_NAN = 31; |
||
513 | // Value of combination field for NaN |
||
514 | var COMBINATION_SNAN = 32; |
||
515 | // decimal128 exponent bias |
||
516 | var EXPONENT_BIAS = 6176; |
||
517 | |||
518 | View Code Duplication | Decimal128.prototype.toString = function() { |
|
519 | // Note: bits in this routine are referred to starting at 0, |
||
520 | // from the sign bit, towards the coefficient. |
||
521 | |||
522 | // bits 0 - 31 |
||
523 | var high; |
||
524 | // bits 32 - 63 |
||
525 | var midh; |
||
526 | // bits 64 - 95 |
||
527 | var midl; |
||
528 | // bits 96 - 127 |
||
529 | var low; |
||
530 | // bits 1 - 5 |
||
531 | var combination; |
||
532 | // decoded biased exponent (14 bits) |
||
533 | var biased_exponent; |
||
534 | // the number of significand digits |
||
535 | var significand_digits = 0; |
||
536 | // the base-10 digits in the significand |
||
537 | var significand = new Array(36); |
||
538 | for(var i = 0; i < significand.length; i++) significand[i] = 0; |
||
539 | // read pointer into significand |
||
540 | var index = 0; |
||
541 | |||
542 | // unbiased exponent |
||
543 | var exponent; |
||
544 | // the exponent if scientific notation is used |
||
545 | var scientific_exponent; |
||
546 | |||
547 | // true if the number is zero |
||
548 | var is_zero = false; |
||
549 | |||
550 | // the most signifcant significand bits (50-46) |
||
551 | var significand_msb; |
||
552 | // temporary storage for significand decoding |
||
553 | var significand128 = {parts: new Array(4)}; |
||
554 | // indexing variables |
||
555 | var i; |
||
556 | var j, k; |
||
557 | |||
558 | // Output string |
||
559 | var string = []; |
||
560 | |||
561 | // Unpack index |
||
562 | var index = 0; |
||
563 | |||
564 | // Buffer reference |
||
565 | var buffer = this.bytes; |
||
566 | |||
567 | // Unpack the low 64bits into a long |
||
568 | low = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
||
569 | midl = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
||
570 | |||
571 | // Unpack the high 64bits into a long |
||
572 | midh = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
||
573 | high = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
||
574 | |||
575 | // Unpack index |
||
576 | var index = 0; |
||
577 | |||
578 | // Create the state of the decimal |
||
579 | var dec = { |
||
580 | low: new Long(low, midl), |
||
581 | high: new Long(midh, high) }; |
||
582 | |||
583 | if(dec.high.lessThan(Long.ZERO)) { |
||
584 | string.push('-'); |
||
585 | } |
||
586 | |||
587 | // Decode combination field and exponent |
||
588 | combination = (high >> 26) & COMBINATION_MASK; |
||
589 | |||
590 | if((combination >> 3) == 3) { |
||
591 | // Check for 'special' values |
||
592 | if(combination == COMBINATION_INFINITY) { |
||
593 | return string.join('') + "Infinity"; |
||
594 | } else if(combination == COMBINATION_NAN) { |
||
595 | return "NaN"; |
||
596 | } else { |
||
597 | biased_exponent = (high >> 15) & EXPONENT_MASK; |
||
598 | significand_msb = 0x08 + ((high >> 14) & 0x01); |
||
599 | } |
||
600 | } else { |
||
601 | significand_msb = (high >> 14) & 0x07; |
||
602 | biased_exponent = (high >> 17) & EXPONENT_MASK; |
||
603 | } |
||
604 | |||
605 | exponent = biased_exponent - EXPONENT_BIAS; |
||
606 | |||
607 | // Create string of significand digits |
||
608 | |||
609 | // Convert the 114-bit binary number represented by |
||
610 | // (significand_high, significand_low) to at most 34 decimal |
||
611 | // digits through modulo and division. |
||
612 | significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); |
||
613 | significand128.parts[1] = midh; |
||
614 | significand128.parts[2] = midl; |
||
615 | significand128.parts[3] = low; |
||
616 | |||
617 | if(significand128.parts[0] == 0 && significand128.parts[1] == 0 |
||
618 | && significand128.parts[2] == 0 && significand128.parts[3] == 0) { |
||
619 | is_zero = true; |
||
620 | } else { |
||
621 | for(var k = 3; k >= 0; k--) { |
||
622 | var least_digits = 0; |
||
623 | // Peform the divide |
||
624 | var result = divideu128(significand128); |
||
625 | significand128 = result.quotient; |
||
626 | least_digits = result.rem.low_; |
||
627 | |||
628 | // We now have the 9 least significant digits (in base 2). |
||
629 | // Convert and output to string. |
||
630 | if(!least_digits) continue; |
||
631 | |||
632 | for(var j = 8; j >= 0; j--) { |
||
633 | // significand[k * 9 + j] = Math.round(least_digits % 10); |
||
634 | significand[k * 9 + j] = least_digits % 10; |
||
635 | // least_digits = Math.round(least_digits / 10); |
||
636 | least_digits = Math.floor(least_digits / 10); |
||
637 | } |
||
638 | } |
||
639 | } |
||
640 | |||
641 | // Output format options: |
||
642 | // Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd |
||
643 | // Regular - ddd.ddd |
||
644 | |||
645 | if(is_zero) { |
||
646 | significand_digits = 1; |
||
647 | significand[index] = 0; |
||
648 | } else { |
||
649 | significand_digits = 36; |
||
650 | var i = 0; |
||
651 | |||
652 | while(!significand[index]) { |
||
653 | i++; |
||
654 | significand_digits = significand_digits - 1; |
||
655 | index = index + 1; |
||
656 | } |
||
657 | } |
||
658 | |||
659 | scientific_exponent = significand_digits - 1 + exponent; |
||
660 | |||
661 | // The scientific exponent checks are dictated by the string conversion |
||
662 | // specification and are somewhat arbitrary cutoffs. |
||
663 | // |
||
664 | // We must check exponent > 0, because if this is the case, the number |
||
665 | // has trailing zeros. However, we *cannot* output these trailing zeros, |
||
666 | // because doing so would change the precision of the value, and would |
||
667 | // change stored data if the string converted number is round tripped. |
||
668 | |||
669 | if(scientific_exponent >= 34 || scientific_exponent <= -7 || |
||
670 | exponent > 0) { |
||
671 | // Scientific format |
||
672 | string.push(significand[index++]); |
||
673 | significand_digits = significand_digits - 1; |
||
674 | |||
675 | if(significand_digits) { |
||
676 | string.push('.'); |
||
677 | } |
||
678 | |||
679 | for(var i = 0; i < significand_digits; i++) { |
||
680 | string.push(significand[index++]); |
||
681 | } |
||
682 | |||
683 | // Exponent |
||
684 | string.push('E'); |
||
685 | if(scientific_exponent > 0) { |
||
686 | string.push('+' + scientific_exponent); |
||
687 | } else { |
||
688 | string.push(scientific_exponent); |
||
689 | } |
||
690 | } else { |
||
691 | // Regular format with no decimal place |
||
692 | if(exponent >= 0) { |
||
693 | for(var i = 0; i < significand_digits; i++) { |
||
694 | string.push(significand[index++]); |
||
695 | } |
||
696 | } else { |
||
697 | var radix_position = significand_digits + exponent; |
||
698 | |||
699 | // non-zero digits before radix |
||
700 | if(radix_position > 0) { |
||
701 | for(var i = 0; i < radix_position; i++) { |
||
702 | string.push(significand[index++]); |
||
703 | } |
||
704 | } else { |
||
705 | string.push('0'); |
||
706 | } |
||
707 | |||
708 | string.push('.'); |
||
709 | // add leading zeros after radix |
||
710 | while(radix_position++ < 0) { |
||
711 | string.push('0'); |
||
712 | } |
||
713 | |||
714 | for(var i = 0; i < significand_digits - Math.max(radix_position - 1, 0); i++) { |
||
715 | string.push(significand[index++]); |
||
716 | } |
||
717 | } |
||
718 | } |
||
719 | |||
720 | return string.join(''); |
||
721 | } |
||
722 | |||
723 | Decimal128.prototype.toJSON = function() { |
||
724 | return { "$numberDecimal": this.toString() }; |
||
725 | } |
||
726 | |||
727 | module.exports = Decimal128; |
||
728 | module.exports.Decimal128 = Decimal128; |
||
729 |