You should multiply the numerator to accommodate the number of digits you need, perform the division and then divide with normal floating point division.
var a = 12340000000000000000n;
var b = 1000000000000000000n;
console.log(Number(a * 100n / b) / 100);
By only converting from BigInt to Number at the "end", you will lose the least precision.
More precision
If you need more than 16 digits precision and need decimals, then you'll need to throw your own implementation of a kind of BigDecimal API, or use an existing one.
Here is a simple one using BigInt as its base type, combined with a configuration that determines how many digits (from the right) of each such BigInt should be interpreted as decimals (digits in the fractional part). That last information will for instance be used to insert a decimal separator when outputting the number as a string.
class BigDecimal {
// Configuration: private constants
static #DECIMALS = 18; // Number of decimals on all instances
static #SHIFT = 10n ** BigInt(BigDecimal.#DECIMALS); // Derived constant
static #fromBigInt = Symbol(); // Secret to allow construction with given #n value
#n; // the BigInt that will hold the BigDecimal's value multiplied by #SHIFT
constructor(value, convert) {
if (value instanceof BigDecimal) return value;
if (convert === BigDecimal.#fromBigInt) { // Can only be used within this class
this.#n = value;
return;
}
const [ints, decis] = String(value).split(".").concat("");
this.#n = BigInt(ints + decis.padEnd(BigDecimal.#DECIMALS, "0")
.slice(0, BigDecimal.#DECIMALS));
}
divide(num) {
return new BigDecimal(this.#n * BigDecimal.#SHIFT / new BigDecimal(num).#n, BigDecimal.#fromBigInt);
}
toString() {
let s = this.#n.toString().replace("-", "").padStart(BigDecimal.#DECIMALS+1, "0");
s = (s.slice(0, -BigDecimal.#DECIMALS) + "." + s.slice(-BigDecimal.#DECIMALS))
.replace(/(\.0*|0+)$/, "");
return this.#n < 0 ? "-" + s : s;
}
}
// Demo
const a = new BigDecimal("123456789123456789876");
const b = new BigDecimal( "10000000000000000000");
console.log(a.divide(b).toString());
Addendum: in a later Q&A I enriched this class with add, subtract, multiply and rounding features.
Answer from trincot on Stack OverflowYou should multiply the numerator to accommodate the number of digits you need, perform the division and then divide with normal floating point division.
var a = 12340000000000000000n;
var b = 1000000000000000000n;
console.log(Number(a * 100n / b) / 100);
By only converting from BigInt to Number at the "end", you will lose the least precision.
More precision
If you need more than 16 digits precision and need decimals, then you'll need to throw your own implementation of a kind of BigDecimal API, or use an existing one.
Here is a simple one using BigInt as its base type, combined with a configuration that determines how many digits (from the right) of each such BigInt should be interpreted as decimals (digits in the fractional part). That last information will for instance be used to insert a decimal separator when outputting the number as a string.
class BigDecimal {
// Configuration: private constants
static #DECIMALS = 18; // Number of decimals on all instances
static #SHIFT = 10n ** BigInt(BigDecimal.#DECIMALS); // Derived constant
static #fromBigInt = Symbol(); // Secret to allow construction with given #n value
#n; // the BigInt that will hold the BigDecimal's value multiplied by #SHIFT
constructor(value, convert) {
if (value instanceof BigDecimal) return value;
if (convert === BigDecimal.#fromBigInt) { // Can only be used within this class
this.#n = value;
return;
}
const [ints, decis] = String(value).split(".").concat("");
this.#n = BigInt(ints + decis.padEnd(BigDecimal.#DECIMALS, "0")
.slice(0, BigDecimal.#DECIMALS));
}
divide(num) {
return new BigDecimal(this.#n * BigDecimal.#SHIFT / new BigDecimal(num).#n, BigDecimal.#fromBigInt);
}
toString() {
let s = this.#n.toString().replace("-", "").padStart(BigDecimal.#DECIMALS+1, "0");
s = (s.slice(0, -BigDecimal.#DECIMALS) + "." + s.slice(-BigDecimal.#DECIMALS))
.replace(/(\.0*|0+)$/, "");
return this.#n < 0 ? "-" + s : s;
}
}
// Demo
const a = new BigDecimal("123456789123456789876");
const b = new BigDecimal( "10000000000000000000");
console.log(a.divide(b).toString());
Addendum: in a later Q&A I enriched this class with add, subtract, multiply and rounding features.
Here's a helper function that cuts down both numbers by the same factor until they can be converted and divided without error.
function bigint_divide(/** @type {bigint} */ num, /** @type {bigint} */ dden) {
let max_int = BigInt(Number.MAX_SAFE_INTEGER)
let num_abs = num * (num > 0n ? 1n : -1n)
let den_abs = den * (den > 0n ? 1n : -1n)
let bigger_abs = num_abs > den_abs ? num_abs : den_abs
let factor = 1n
while(bigger_abs > max_int) {
factor *= 10n
bigger_abs /= 10n
}
num /= factor
den /= factor
return Number(num) / Number(den)
}
There may be precision lost by applying the factor but it's the only way to get to a floating point value somehow. This function mathematically does the same as the multiplication solution from the accepted answer, but finds a fitting factor itself without the caller having to worry about it.
» npm install divide-bigint
The standard reference for big-integer arithmetic is Donald Knuth's book Art of Computer Programming, Volume 2, Section 4.3. His division algorithm is basically the grade-school algorithm, with some small improvements.
By the way, most people that implement big-integer arithmetic use a power of two rather than a power of ten as the radix of their number system.
I'd suggest you have a look at the source code of the GMP library and port the functionality you need to JavaScript, or get an idea of how it's done.
If there is a good algorithm out there, this library will most likely have it; and it is distributed under the LGPL.
In general...
...mixing number and BigInt is asking for trouble, which is why no math operators will let you do it and explicit conversion is required.
(Note: Decimal is on the horizon, which will apply here when it matures.)
If you want a number result...
...convert the BigInt to a number before doing the calculation; beware that the conversion may be lossy for very large numbers (this is the point of BigInt), specifically numbers above Number.MAX_SAFE_INTEGER or below Number.MIN_SAFE_INTEGER:
let n = 100n;
let x = 0.1;
let result = Number(n) / x;
console.log(result);
If you want a BigInt result...
...it's more complicated, because you have to decide what to do with a fractional result. (Your example won't have a fractional result, but the general case will.) You could go through number, but again that's a potentially lossy operation:
let n = 100n;
let x = 0.1;
let result = BigInt(Number(n) / x); // Throws if the result is fractional
// let result = BigInt(Math.round(Number(n) / x)); // Rounds instead of throwing
console.log(result.toString());
If you can refactor the operation so you can express it in whole numbers, that makes it a lot easier, because then you can make x a BigInt. For instance, in your specific case, / 0.1 is the same as * (1 / 0.1) which is * 10:
let n = 100n;
let x = 10n;
let result = n * x;
console.log(result.toString());
...but that's just that specific case.
You'll probably find you need to handle it on a case-by-case basis, trying to avoid doing the operation. When you can't, and the divisor is fractional, a round-trip through number may be unavoidable.
Just Convert the bigint to number. It will Work.
let n = 100n;
let x = 0.1;
console.log(Number(n)/x);
It will Return Result 1000