-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
I've written a full BigDecimal implementation polyfill. #82
Comments
A follow up to my post. Two things to cover. Why I used stringsFirst up, for anyone who may take a look at this, you might wonder why I used strings to store the value. There is some logic to this. I feel I should explain why. As I mentioned in my orginal post, my
was more performant than
So I just continued on with the string based version. Then, a few days ago, I wondered, out of the blue, "I wonder if BigInt literal experssions are optimised, like numbers are" (constant folding). Just a completely normal, everyday thought. So I wrote a test, run it in node, and output suggested that no, BigInts are not constant folded. I was kinda surprised, so I copied the code into chrome dev tools console. The results were massively different, not only did it fold, but was between 2-3x faster on top of that... I always thought BigInts were a bit too slow, turns out they were, and have been massively improved in V8 engine. The version of node is 18.7.0. So, somewhere between that version of node, and the latest v8 engine, BigInts were massively improved. I want to go through and replace the whole codebase using BigInts. Not only for the performance gain, but it will also improve memory useage a lot, not having strings thrown about like they are! For the time being, I'm going to leave it. If I find myself needing even more performance, or if interest in this library picks up, then I will do it! TL;DR (i dont blame you): And now for thing 2... Division...Immediately after replacing my old functions with this library in my project, I got a problem straight away. My output was now different from before. So I dug in, and yeah, it was division... And it was intermittent, it wasn't all division, just some division. Let's illustrate the problem: The default
So, without overriding the The reason why this wasn't a problem for me before, was because I didn't have a So, this got me thinking that divide needs to behave differently, using significant digits instead. Much like other decimal types in various languages. However, I wasn't sold on the idea of it behaving exactly like other types, where the integral part of the number would be rounded to a certain precision, getting results with trailing zeros like What I've done is, I've made a branch called
Each one behaves differently, and they use In operator tests, there's a section that shows the differences in behaviour: |
This is amazing work, thank you! The current thinking for decimal is that we'd like to align with IEEE 754 fixed bit-width Decimal128 rather than with unlimited BigDecimal. I have an NPM library that you can use to experiment with these kinds of decimals. |
Hiya
https://github.com/NoUJoe/JSBD
This is a full implementation of BigDecimal (and not BigDecimal128)
Let me give a rundown of why I made this and then a rundown of the library.
I was working on a personal project, and needed decimal precision, specificlly add and multiply. Due to it being a personal project, I decided to take on the challenge and implement my own functions. I literally called them "MultiplyTwoStrings" and "AddTwoStrings". These functions became staples in my next few personal projects, I improved them over time too. On one project, I'm trying to sort a bottleneck, and finally decide it's time to concede and use a decimal library, because it will obviously be faster than my code. So I download some decimal libraries (I think the 2 most popular, whatever shows at the top of google/npm). I wrote a test to compare performance vs my functions (add and multiply)... Low and behold, to my utter surprise, mine were faster... I remember sitting there just sorta tilting my head thinking "huh...". I can't remember how much faster, it wasn't like ground breaking 300x or anything, but the main point being, there was no point swapping my functions out for a library.
Blah blah blah, jump forward and I've added a "DivideTwoStrings" and a couple of comparison functions. At some point, I have an epiphany, my functions take strings and return strings (if the names didn't give it away), and the string inputs need to be processed every function call. I can remove this processing if I make a class/object/whatever javascript terminology, and save even more time processing wise. Really obvious, I know... But sometimes it takes a year or so of staring at the same code for it to click...
So I decide to finally make a class, JSBI springs to mind, so I have a search and find this proposal. Thought that I'm doing this anyway, so why not implement this proposal?
And here we are, all caught up!
Ok, so, the library. It is essentially an implementation of this: https://github.com/tc39/proposal-decimal/blob/main/bigdecimal-reference.md
There are a few minor differences, I'll explain them first
I included a
decimals
instance property, which returns the amount of decimal places the number has.(1.12345m).decimals === 5
roundingOptions
is different from the proposal, this is because there is prescedent for this in Javascript. See https://developer.mozilla.org/en-US/docs/Web/Javascript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#roundingmodeThe
roundingOptions
interface is{ roundingMode, roundingIncrement, maxFractionDigits }
See the MDN link which has all the
roundingMode
androundingIncrement
valuesI included a few extra operators. I did this for "fullness", I'd rather present as full of a implementation as possible, things can always be removed easily.
Other than that, it's to the T. I'll paste all methods/properties at the end, I'm not going to explain them all. The only two that need explaining are:
logicalNot2()
andbitwiseNot2()
. These are shortcuts for!!
and~~
respectively. WithlogcialNot()
andbitwiseNot()
being the single versions.It's thoroughly tested, I have ~8k lines of tests, across 4 files, in the "tests" folder. (and yes, I used 69.6969 A LOT in the rounding tests, a guy's allowed a little fun when testing 9 rounding modes)
Written in pure Javascript, with a complete type definition file.
I think that covers most of it. Honestly, if anyone is interested/wants to know more, you should just check out the repo. The test files are organised extremely neatly for the purpose of showcasing. The repo is set up as a node module so you should be able to:
npm i "https://github.com/NoUJoe/JSBD"
It's ESM only right now, I've done no transpiling or the likes. It's a very basic setup.
And to wrap up. I have 0 intention of releasing this myself as a package on npm. That's why I'm posting here. It's my offer back to the community. I'm going to use this current version in my projects (my original reason for starting this), but other than that, it's honestly not in my interest to release/maintain a module.
So if you're interested, let me know, and we can take it from there!
And here's the full API:
The text was updated successfully, but these errors were encountered: