19

Here's what I've tried so far. I'm looking to get a 12.34:

BigInt('12340000000000000000') / BigInt('1000000000000000000')

12n

Number(BigInt('12340000000000000000') / BigInt('1000000000000000000'))

12

FWIW, when I use the JSBI lib, it's working how I'd like:

JSBI.BigInt('12340000000000000000') / JSBI.BigInt('1000000000000000000');

12.34

Is that not possible natively?

robmisio
  • 926
  • 1
  • 10
  • 20
  • 2
    What about `Number(BigInt('x')) / Number(BigInt('y'))` - that is, a plain native divide of two native numbers? – Pointy Jan 28 '19 at 20:35
  • 1
    Otherwise as far as I can tell you cannot do that, as there is nothing related to `BigInt` as `BigDecimal` is related to `BigInteger` in Java – Pointy Jan 28 '19 at 20:36
  • 3
    @Pointy `Number(BigInt('12340000000000000001'))` wouldn't work because the whole point of using a BigInt is to maintain the full precision which you'll lose if you just convert it to a number. The result of that would be `12340000000000000000`. – robmisio Jan 28 '19 at 20:40
  • 1
    @Pointy To your second comment... yeah, I imagine it might not be doable, but weird that the lib that is officially recommended as the fallback could do it – robmisio Jan 28 '19 at 20:41
  • `but weird that the lib that is officially recommended as the fallback could do it` That's because your then just doing `Number / Number`,.. eg. You can do that calc without using JSBI.. In pure JS -> `12340000000000000000 / 1000000000000000000 = 12.34` – Keith Jan 28 '19 at 20:47
  • 2
    The equivalent in JSBI would be `JSBI.divide('12340000000000000000', '1000000000000000000')` – Keith Jan 28 '19 at 20:51

1 Answers1

27

You should multiply the numerator to accommodate the number of digits you need, perform the division and then divide with normal floating point division.

(Run in browser that supports BigInt, like Chrome)

var a = 12340000000000000000n;
var b =  1000000000000000000n;

console.log(Number(a * 100n / b) / 100);

By only converting 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 {
    constructor(value) {
        let [ints, decis] = String(value).split(".").concat("");
        decis = decis.padEnd(BigDecimal.decimals, "0");
        this.bigint = BigInt(ints + decis);
    }
    static fromBigInt(bigint) {
        return Object.assign(Object.create(BigDecimal.prototype), { bigint });
    }
    divide(divisor) { // You would need to provide methods for other operations
        return BigDecimal.fromBigInt(this.bigint * BigInt("1" + "0".repeat(BigDecimal.decimals)) / divisor.bigint);
    }
    toString() {
        const s = this.bigint.toString().padStart(BigDecimal.decimals+1, "0");
        return s.slice(0, -BigDecimal.decimals) + "." + s.slice(-BigDecimal.decimals)
                .replace(/\.?0+$/, "");
    }
}
BigDecimal.decimals = 18; // Configuration of the number of decimals you want to have.

// Demo
var a = new BigDecimal("123456789123456789876");
var b = new BigDecimal( "10000000000000000000");

console.log(a.divide(b).toString());

Again, this needs a browser that supports BigInt (Chrome at the time of writing).

trincot
  • 263,463
  • 30
  • 215
  • 251
  • Now how can I do the same thing if I need 18 digit precision (e.g. down to .000000000000000001) if that would make that multiplier/divider in and of itself a BigInt? – robmisio Jan 29 '19 at 19:00
  • 1
    See addition to my answer. – trincot Jan 29 '19 at 20:22
  • In typescript this will return ".12345678912345678987" – noririco Apr 04 '21 at 06:14
  • 1
    @noririco, It gives the same output in [typescript](https://replit.com/@trincottrincots/httpsstackoverflowcoma544099775459839#index.ts): "12.345678912345678987" – trincot Apr 04 '21 at 07:09
  • 1
    @trincot THANKS! my mistake was `static decimals = 18;` was not `static` – noririco Apr 04 '21 at 07:57
  • @trincot another q: if I calc `10000000 / 4294967296` I get `.2328306436538696` instead of `0.002328306436538696`, what can be the cause for this ? – noririco Apr 05 '21 at 06:46
  • 1
    The cause was that the string `s` in `toString` did not have enough digits to inject a decimal point at the right position. Fixed now. Thanks for spotting that. – trincot Apr 05 '21 at 07:19
  • 2
    FYI, I have a version with add,subtract,multiply, and rounding as answer to [BigDecimal in JavaScript](https://stackoverflow.com/a/66939244/5459839). – trincot Apr 05 '21 at 07:45
  • @trincot GOD bless you and GOD bless the United States of stackoverflow – noririco Apr 05 '21 at 08:11