@amazon-dax-sdk/client-dax
Version:
Amazon DAX Client for JavaScript
181 lines (167 loc) • 5.64 kB
JavaScript
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License
* is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
'use strict';
const BigNumber = require('bignumber.js');
const DaxClientError = require('./DaxClientError');
const DaxErrorCode = require('./DaxErrorCode');
const TEN = new BigNumber(10);
const ONE_TENTH = new BigNumber(0.1);
const EXPREP_DEFAULT = 6;
class BigDecimal {
constructor(obj, scale) {
this._EXPREP = EXPREP_DEFAULT;
if(obj instanceof BigNumber) {
this.unscaledValue = obj;
this.scale = parseInt(scale);
return;
} else if(typeof obj !== 'string') {
throw new DaxClientError('should be initialized by either string or unscaledValue and scale', DaxErrorCode.IllegalArgument);
}
let str = obj;
let exppoint = str.length;
let decimalpoint = undefined;
for(let i = 0; i < str.length; ++i) {
if(str[i] === '.') {
decimalpoint = i;
} else if(str[i] === 'e' || str[i] === 'E') {
exppoint = i;
}
}
let escale = 0;
if(decimalpoint === undefined) { // no decimal point
decimalpoint = exppoint;
this.scale = 0;
} else {
this.scale = exppoint - decimalpoint - 1;
}
if(exppoint !== str.length) {
escale = parseInt(str.slice(exppoint + 1, str.length));
}
// at this step scale will always be non-negative
// this.unscaledValue = new BigInteger(str.slice(0, exppoint)).pow(this.scale);
this.unscaledValue = new BigNumber(str.slice(0, decimalpoint) + str.slice(decimalpoint + 1, exppoint));
this.scale -= escale;
}
config(obj) {
if(!obj['EXPREP']) {
this._EXPREP = obj['EXPREP'];
}
}
toString() {
if(this.unscaledValue.equals(0)) {
return '0';
}
// get plain string of unscaledValue
let str = this.unscaledValue.toPrecision(this.unscaledValue.e + 1);
if(this.scale === 0) {
return str;
}
let sign = ''; // sign
if(str[0] === '-') {
sign = '-';
str = str.slice(1);
}
if(this.scale < 0) { // add positive "E" afterwards
let afterDecimal = '.' + str.slice(1);
if(afterDecimal === '.') {
afterDecimal = '';
}
return sign + str[0] + afterDecimal + `E${str.length - this.scale - 1}`;
} else if(this.scale < str.length) {
// put decimal point somewhere in between
let afterDecimal = '.' + str.slice(str.length - this.scale);
if(afterDecimal === '.') {
afterDecimal = '';
}
return sign + str.slice(0, str.length - this.scale) + afterDecimal;
} else {
// put decimal point in front of, which means add 'E' back
let escale = str.length - this.scale - 1;
if(-escale <= this._EXPREP) {
// use decimal instead of scientific representation
return sign + this._precedingZeros(-escale) + str;
} else {
let afterDecimal = '.' + str.slice(1);
if(afterDecimal === '.') {
afterDecimal = '';
}
afterDecimal += `E${escale}`;
return sign + str[0] + afterDecimal;
}
}
}
toJSON() {
return this.toString();
}
equals(obj) { // equal means not only value but also precision is the same
if(!(obj instanceof BigDecimal)) {
return false;
}
if(this.unscaledValue.isZero()) {
return obj.unscaledValue.isZero();
}
return this.unscaledValue.equals(obj.unscaledValue) && this.scale === obj.scale;
}
// in the future, we need to use our own BigInteger, then this function need
// to be uncomment again, to compare BigDecimal
// _getIntAndDec() {
// let int, dec
// if (this.scale <= 0) {
// int = this.unscaledValue.pow(-this.scale).toStringAbs()
// dec = '0'
// } else {
// let as = this.unscaledValue.toStringAbs()
// if (as.length > this.scale) {
// int = as.slice(0, as.length - this.scale)
// dec = as.slice(as.length - this.scale)
// } else {
// int = '0'
// dec = '0'.repeat(this.scale - as.length) + as
// }
// }
// return [int, dec]
// }
// comparedTo(obj) {
// if (this.unscaledValue.isNeg() !== obj.unscaledValue.isNeg()) {
// if (this.unscaledValue.isNeg()) return -1
// else return 1
// }
// let [ai, ad] = this._getIntAndDec()
// let [bi, bd] = obj._getIntAndDec()
// let intbigger = ((ai.length === bi.length) ? ai.localeCompare(bi) : (ai.length - bi.length))
// if (intbigger === 0) {
// let decbigger = ad.localeCompare(bd)
// return (this.unscaledValue.isNeg() ? -decbigger : decbigger)
// } else
// return (this.unscaledValue.isNeg() ? -intbigger : intbigger)
// }
comparedTo(obj) {
let a = this.scale < 0 ?
this.unscaledValue.mul(TEN.pow(-this.scale)) :
this.unscaledValue.mul(ONE_TENTH.pow(this.scale));
let b = obj.scale < 0 ?
obj.unscaledValue.mul(TEN.pow(-obj.scale)) :
obj.unscaledValue.mul(ONE_TENTH.pow(obj.scale));
return a.comparedTo(b);
}
_precedingZeros(n) {
let str = '0.';
for(let i = 1; i < n; ++i) {
str += '0';
}
return str;
}
}
module.exports = BigDecimal;