26

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
  • 1,066
  • 2
  • 12
  • 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
  • 4
    @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

36

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
  • 317,000
  • 35
  • 244
  • 286
  • 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
  • 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
  • Just for anyone that may read this later, it seems multiplying by 100n doesn't work anymore, so use BigInt(100) instead. – Baba Dan Constantin May 28 '23 at 10:21
  • @BabaDanConstantin, what are you referring to? Multiplying by 100n works fine. If it doesn't work for you then you are running on some JavaScript implementation that is not compliant with ECMAScript 2020+ specifications. – trincot May 28 '23 at 10:39
  • I was using React and it told me I shouldn't mix BigInt with some other type or something similar to this. React@18.2 – Baba Dan Constantin May 28 '23 at 11:08
  • @BabaDanConstantin, that is an error you would also get without react, but in my answer there is no mixing of data types, so apparently you are doing something different with your bigints. – trincot May 28 '23 at 11:55
  • ```useEffect(() => { // sets percentageExp to a radius between 0 and 360deg. const result = Number((experience * BigInt(100)) / experienceNextLevel) / 100; // Handle approximations errors. if (result < 0) setPercentageExp(0); else if (result > 1) setPercentageExp(360); else { setPercentageExp(Math.floor(result * 360)); } }, [experience, experienceNextLevel]);``` – Baba Dan Constantin May 28 '23 at 12:24
  • @BabaDanConstantin, the comment section is not ideal to paste multi-line pieces of code. If you really have a question about that code and the error you get, then I suggest you ask a new question, specifying also how each involved variable is defined. My guess would be that `experience` is not a bigint, and then it is normal you get an error. This has nothing to do with my answer. – trincot May 28 '23 at 12:34
  • Then why did you reply? I only left my message for other people that may face the same issue, not to get an answer, since I found the solution myself. – Baba Dan Constantin May 28 '23 at 12:35
  • Comments should relate to the answer. Now you give the impression with your first comment that my answer is in some way not behaving correctly. And I reply so that other people don't get that wrong idea about my answer. That "it doesn't work anymore" is just a plain wrong statement. – trincot May 28 '23 at 12:36