Comparing version 0.0.1 to 0.0.2
{ | ||
"name": "bigfixed", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "big fixed point number base on BigInt", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -15,2 +15,6 @@ # BigFixed | ||
> NOTE: this lib is calculate by binary but not decimal number, | ||
`BigFixed(0.1).add(0.2).toString() === "0.30000000000000004"` | ||
## [Compare](https://github.com/GeekBerry/bigFixed/blob/master/example/compare.js) | ||
@@ -20,14 +24,14 @@ | ||
Compare of big number lib | ||
test number: 5073.876555537511, | ||
test number: 3838.261003615915, | ||
test count: 100000 | ||
Each duration in nano seconds (1e-9s): | ||
Execution time in nano seconds(1e-9s): | ||
Operate BigFixed BigNumber Big Decimal | ||
new 3340 700 360 580 | ||
add 130 220 350 290 | ||
sub 120 240 510 460 | ||
mul 320 410 1120 680 | ||
div 920 3810 7620 3480 | ||
pow 480 1340 3990 2290 | ||
str 1060 210 430 260 | ||
new 580 750 410 690 | ||
add 90 210 240 270 | ||
sub 120 300 490 500 | ||
mul 210 420 1220 710 | ||
div 800 3760 6720 3360 | ||
pow 380 1360 3950 2330 | ||
str 530 220 410 250 | ||
``` | ||
@@ -34,0 +38,0 @@ |
275
src/index.js
@@ -1,265 +0,24 @@ | ||
/* | ||
BigFixed 是基于 BigInt 实现的定点数, 小数部分保留 64 bit, | ||
即类比于 fixed = float << 64 | ||
const Class = require('./BigFixed'); | ||
BigFixed range: ( -Inf, -(2**(-64)) ] & 0 & [ 2**(-64), +Inf ) | ||
BigFixed struct: | ||
+--------------------------------+----------------------------------+ | ||
| signed N bits for integer part | signed 64 bits for fraction part | | ||
+--------------------------------+----------------------------------+ | ||
*/ | ||
const { | ||
abs, | ||
slice, | ||
fractionNumber, | ||
compileBigInt, | ||
compileBoolean, | ||
compileNumber, | ||
compileString, | ||
} = require('./util'); | ||
const UINT64_HALF = BigInt(1) << BigInt(63); | ||
const UINT64_TWOS = BigInt(1) << BigInt(64); | ||
/** | ||
* Cause javascript use float64 to implement `Number`, | ||
* if value is number, `BigFixed` parse number binary data by **ieee-754** | ||
* Function for both new and call can create instance. | ||
* use `new Proxy(BigFixed, { apply: (Class, __, args) => new Class(...args) })` is too slow | ||
* | ||
* @param value {number|boolean|BigInt|string|BigFixed} | ||
* @return {BigInt} | ||
* @param args | ||
* @return {BigFixed} | ||
* @constructor | ||
* | ||
* @example | ||
* > new BigFixed(1) | ||
BigFixed { fixed: 18446744073709551616n } | ||
* > BigFixed(1) | ||
BigFixed { fixed: 18446744073709551616n } | ||
*/ | ||
function toFixed(value) { | ||
if (value === undefined || value === null) { | ||
throw new Error(`unexpected type ${value}`); | ||
} | ||
if (value instanceof BigFixed) { | ||
return value.fixed; | ||
} | ||
if (Number.isFinite(value)) { | ||
return compileNumber(value); | ||
} | ||
if (value === false || value === true) { | ||
return compileBoolean(value); | ||
} | ||
if (value.constructor === BigInt) { | ||
return compileBigInt(value); | ||
} | ||
return compileString(`${value}`); | ||
function BigFixed(...args) { | ||
return new Class(...args); | ||
} | ||
// ============================================================================ | ||
class BigFixed { | ||
constructor(value) { | ||
this.fixed = toFixed(value); | ||
} | ||
BigFixed.prototype = Class.prototype; | ||
Object.assign(BigFixed, Class); | ||
clone() { | ||
return new BigFixed(this); | ||
} | ||
// -------------------------------------------------------------------------- | ||
isZero() { | ||
return this.fixed === BigInt(0); | ||
} | ||
isNegative() { | ||
return this.fixed < BigInt(0); | ||
} | ||
isInteger() { | ||
return slice(this.fixed, -64) === BigInt(0); | ||
} | ||
eq(value) { | ||
return this.fixed === toFixed(value); | ||
} | ||
lt(value) { | ||
return this.fixed < toFixed(value); | ||
} | ||
lte(value) { | ||
return this.fixed <= toFixed(value); | ||
} | ||
gt(value) { | ||
return this.fixed > toFixed(value); | ||
} | ||
gte(value) { | ||
return this.fixed >= toFixed(value); | ||
} | ||
// ------------------------------- Arithmetic ----------------------------- | ||
neg() { | ||
const bn = this.clone(); | ||
bn.fixed = -bn.fixed; | ||
return bn; | ||
} | ||
abs() { | ||
const bn = this.clone(); | ||
bn.fixed = abs(bn.fixed); | ||
return bn; | ||
} | ||
add(value) { | ||
const bn = this.clone(); | ||
bn.fixed += toFixed(value); | ||
return bn; | ||
} | ||
sub(value) { | ||
const bn = this.clone(); | ||
bn.fixed -= toFixed(value); | ||
return bn; | ||
} | ||
mul(value) { | ||
const bn = this.clone(); | ||
bn.fixed *= toFixed(value); | ||
return bn.rShift(64); | ||
} | ||
div(value) { | ||
const bn = this.lShift(64); | ||
bn.fixed /= toFixed(value); | ||
return bn; | ||
} | ||
mod(value) { | ||
value = toFixed(value); | ||
const bn = this.clone(); | ||
bn.fixed -= (bn.fixed / value) * value; | ||
return bn; | ||
} | ||
pow(value) { | ||
value = BigInt(value); | ||
const bn = this.clone(); | ||
bn.fixed = (bn.fixed ** value) >> (BigInt(64) * value - BigInt(64)); | ||
return bn; | ||
} | ||
// -------------------------------- Logical ------------------------------- | ||
not() { | ||
const bn = this.clone(); | ||
bn.fixed = ~bn.fixed; | ||
return bn; | ||
} | ||
and(value) { | ||
const bn = this.clone(); | ||
bn.fixed &= toFixed(value); | ||
return bn; | ||
} | ||
or(value) { | ||
const bn = this.clone(); | ||
bn.fixed |= toFixed(value); | ||
return bn; | ||
} | ||
xor(value) { | ||
const bn = this.clone(); | ||
bn.fixed ^= toFixed(value); | ||
return bn; | ||
} | ||
lShift(count) { | ||
const bn = this.clone(); | ||
bn.fixed <<= BigInt(count); | ||
return bn; | ||
} | ||
rShift(count) { | ||
const bn = this.clone(); | ||
if (bn.isNegative()) { | ||
bn.fixed = -((-bn.fixed) >> BigInt(count)); | ||
} else { | ||
bn.fixed >>= BigInt(count); | ||
} | ||
return bn; | ||
} | ||
// -------------------------------------------------------------------------- | ||
/** | ||
* @param [mode=BigFixed.ROUND] {string} - Round mode, enum [BigFixed.CEIL, BigFixed.ROUND, BigFixed.FLOOR] | ||
* @return {BigFixed} | ||
*/ | ||
toInteger(mode = BigFixed.ROUND) { | ||
const bn = this.clone(); | ||
const frac = slice(bn.fixed, -64); | ||
bn.fixed ^= frac; | ||
switch (mode) { | ||
case BigFixed.CEIL: | ||
if (frac) { | ||
bn.fixed += UINT64_TWOS; | ||
} | ||
break; | ||
case BigFixed.ROUND: | ||
if (frac >= UINT64_HALF) { | ||
bn.fixed += UINT64_TWOS; | ||
} | ||
break; | ||
case BigFixed.FLOOR: | ||
break; | ||
default: | ||
throw new Error(`unexpected round mode "${mode}"`); | ||
} | ||
return bn; | ||
} | ||
/** | ||
* @return {number} | ||
*/ | ||
toNumber() { | ||
return Number(this.toString()); | ||
} | ||
toJSON() { | ||
return this.toString(); | ||
} | ||
/** | ||
* @param [base=10] {number} | ||
* @return {string} | ||
*/ | ||
toString(base = 10) { | ||
const absFixed = abs(this.fixed); | ||
const signString = this.isNegative() ? '-' : ''; | ||
const int = (absFixed >> BigInt(64)); | ||
const fracNumber = fractionNumber(slice(absFixed, -64)); | ||
return `${signString}${int.toString(base)}${fracNumber.toString(base).slice(1)}`; // slice 1 to skip '0' | ||
} | ||
} | ||
BigFixed.CEIL = 'ceil'; | ||
BigFixed.ROUND = 'round'; | ||
BigFixed.FLOOR = 'floor'; | ||
// ============================================================================ | ||
function withoutNew(cls) { | ||
return new Proxy(cls, { apply: (_, __, params) => new cls(...params) }); | ||
} | ||
module.exports = withoutNew(BigFixed); | ||
module.exports = BigFixed; |
@@ -1,14 +0,15 @@ | ||
/* | ||
[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) | ||
const BIG_INT_0 = BigInt(0); | ||
const BIG_INT_64 = BigInt(64); | ||
float64: S EEEEEEEEEEE DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD | ||
1 11 52 | ||
*/ | ||
const UINT64_SIGN = BigInt(1) << BigInt(63); | ||
const UINT64_TWOS = BigInt(1) << BigInt(64); | ||
const UINT64_TWOS_NUMBER = Number(UINT64_TWOS); | ||
const FLOAT64_FRAC_TOWS = BigInt(1) << BigInt(52); | ||
const FLOAT64_EXP_OFFSET = BigInt(1023); // (2**11/2 - 1) | ||
const FLOAT64_FRAC_TOWS = BigInt(1) << BigInt(52); | ||
const UINT64_SIGN = BigInt(1) << BigInt(63); | ||
const FLOAT64_FRAC_OFFSET = BigInt(64) - BigInt(52) - FLOAT64_EXP_OFFSET; | ||
const VIEW64 = new DataView(new ArrayBuffer(8)); // 8=64/8 | ||
// ---------------------------------------------------------------------------- | ||
function float64ToUInt64(number) { | ||
@@ -19,8 +20,6 @@ VIEW64.setFloat64(0, number); | ||
function uInt64ToFloat64(bUInt64) { | ||
VIEW64.setBigUint64(0, bUInt64); | ||
return VIEW64.getFloat64(0); | ||
function abs(bigInt) { | ||
return bigInt < BIG_INT_0 ? -bigInt : bigInt; | ||
} | ||
// ---------------------------------------------------------------------------- | ||
function slice(uInt64, startBit = 0, stopBit = 64) { | ||
@@ -32,42 +31,19 @@ startBit = startBit >= 0 ? startBit : 64 + startBit; | ||
function abs(bigInt) { | ||
return bigInt < BigInt(0) ? -bigInt : bigInt; | ||
} | ||
// ============================================================================ | ||
/** | ||
* Get BigFixed fraction part as number (for toString). | ||
* | ||
* @param bUInt64 {BigInt} | ||
* @return {number} | ||
*/ | ||
function fractionNumber(bUInt64) { | ||
let exp = FLOAT64_EXP_OFFSET; | ||
if (bUInt64) { | ||
while (!(bUInt64 & UINT64_SIGN)) { | ||
bUInt64 <<= BigInt(1); | ||
exp -= BigInt(1); | ||
} | ||
bUInt64 <<= BigInt(1); | ||
exp -= BigInt(1); | ||
} else { | ||
exp = BigInt(0); | ||
} | ||
const fraction = slice(bUInt64, 0, 52); | ||
return uInt64ToFloat64(exp << BigInt(52) | fraction); | ||
} | ||
// ---------------------------------------------------------------------------- | ||
function compileBigInt(bigInt) { | ||
return bigInt << BigInt(64); | ||
return bigInt << BIG_INT_64; | ||
} | ||
function compileBoolean(boolean) { | ||
return boolean ? BigInt(1) << BigInt(64) : BigInt(0); | ||
return boolean ? UINT64_TWOS : BIG_INT_0; | ||
} | ||
/** | ||
* Compile IEEE 754 float64 to fixed64 BigInt | ||
* Compile IEEE 754 float64 to fixed64 BigInt. | ||
* | ||
* [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) | ||
* | ||
* float64: S EEEEEEEEEEE DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD | ||
* 1 11 52 | ||
* | ||
* @param number {number} | ||
@@ -87,3 +63,3 @@ * @return {BigInt} | ||
return (sign ? -fraction : fraction) << (exp - FLOAT64_EXP_OFFSET + BigInt(12)); | ||
return (sign ? -fraction : fraction) << (exp + FLOAT64_FRAC_OFFSET); | ||
} | ||
@@ -99,3 +75,3 @@ | ||
if (/^\s*(0x[0-9a-f]+|0o[0-7]+|0b[01]+|[+-]?[0-9]+)\s*$/i.test(string)) { | ||
return BigInt(string) << BigInt(64); | ||
return BigInt(string) << BIG_INT_64; | ||
} | ||
@@ -109,5 +85,5 @@ | ||
const frac = compileNumber(Number(`0.${fracString}`)); | ||
const fixed = (abs(int) << BigInt(64)) | frac; | ||
const fixed = (abs(int) << BIG_INT_64) | frac; | ||
return int < BigInt(0) ? -fixed : fixed; | ||
return int < BIG_INT_0 ? -fixed : fixed; | ||
} | ||
@@ -120,5 +96,10 @@ | ||
module.exports = { | ||
BIG_INT_0, | ||
BIG_INT_64, | ||
UINT64_SIGN, | ||
UINT64_TWOS, | ||
UINT64_TWOS_NUMBER, | ||
abs, | ||
slice, | ||
fractionNumber, | ||
compileBoolean, | ||
@@ -125,0 +106,0 @@ compileBigInt, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14893
5
334
182
1