Design choices and new features for big upcoming update to bnum #54
Replies: 5 comments 2 replies
-
|
My use case is implementing fixed-point numbers for a custom physics system in a hobby project that will hopefully end up as a completed indie game. The interesting part is that multiplying two numbers gives a widened result type (
I'm all for it if you think it's an improvement. This could potentially enable fixed point types smaller than 64 bits which may be useful for on-disk formats and preventing the calculations from growing too large. My only concern would be how u128 math performs on webassembly, but my game's performance is already abysmal on wasm32 and web won't be relevant to the project for years.
This is not a limitation for my use case 👍
I am already using it 😄
This affects my code, but I am willing to change to update it since it's just implemented as a convenience (i.e.
👍 |
Beta Was this translation helpful? Give feedback.
-
|
Thanks @krakow10! I'm unfamiliar with how u128 math works on web assembly - does it use u64 operations under the hood? |
Beta Was this translation helpful? Give feedback.
-
|
Hi, I am expecting the arbitrary bit widths implementation and arbitrary width floats. This is like Zig's |
Beta Was this translation helpful? Give feedback.
-
|
Hi, one more thing, will |
Beta Was this translation helpful? Give feedback.
-
|
Hi everyone, I do apologise for the delay in releasing this version. I was quite ambitious about the number of new features I wanted to add, and so wasn't realistic with how long it would take. To give an update: the migration to the new integer types is almost complete, and I'm pleased to say that the new integer types have performance that matches or in several cases beats the performance of Another significant change that I'm implementing is unifying the unsigned type Another thing I've been doing is writing documentation for each method, rather than just linking to the Rust primitive equivalent, which will make the docs much clearer and easier to read. I hope that the new version will be ready by the end of the year, but I am about to start my PhD, so am not sure how much time I will be able to spend on it for the next few months. If you have any questions or suggestions, please do let me know. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello everyone! I'm opening this discussion as I have been working on a new version of bnum which will include the biggest changes to the crate so far, and I would love to hear everyone's input on what various design choices which I'm thinking of adding/changing. I'll list the things I'm thinking of changing/adding below: comments on any/all of them would be much appreciated.
Space efficient types that are at least as fast as
BUintandBIntThe biggest change will be that there will only be one unsigned integer type and one signed integer type. These will effectively be the same as
BUintD8andBIntD8, with the same API and layout (i.e. an array ofu8digits). The important difference will be that, where relevant, methods will iterate over batches of 16u8digits at a time, rather than 1 digit at a time. This effectively means the integer will in fact operate onu128digits (16 * 8 = 128) and will use methods onu128rather thanu8ints. To give an example, theBitAndimplementation is a simple digit-wise logical AND. Rather than computing AND for each pair ofu8digits, we compute AND for each pair of "effective"u128digits (formed from 16 consecutiveu8digits). It doesn't matter if the total number of bits is not a multiple of128, as the last effectiveu128digit will be leading zero-padded.BUint(u64digits) of the same size (since we are iterating over 2 times fewer effective digits), while retaining maximum control over the size of the integer. Other benefits are reduced crate size and faster compile times (as there are 4 times fewer integer types), and being able to make the{from,to}_{b,l,n}_e_bytesavailable on stable.U24, it would compute using oneu128rather than 3u8s), but the integer itself would be stored with the desired number of bytes.BUintD8andBIntD8seem unnecessary, as now these are the only two integer types. ButBUintandBIntmight cause confusion as these currently use64bit digit, not8. So perhaps justUintandInt.Allowing arbitrary bit widths
Currently, the bit width has to be a multiple of
8. If being able to specify an arbitrary bit width as another const-generic parameter is of interest, I would consider adding this. If this was implemented, there would be some decisions to make about the API:generic_const_exprsis stabilised, a const-generic parameter specifying the number of digits would be required as well.Uint<Digits, Bits>, whereBitsis by default0, which would signify that the integer usesDigits * 8bits (we can't have the default beDigits * 8as this requiresgeneric_const_exprs. The only thing is having0to meanDigits * 8bits looks a bit strange (although a0bit width integer is not needed so maybe not a big issue).Bitslies in the range(Digits * 8, Digits * 8].Use the 2024 edition of Rust (requires MSRV
1.85.0)const_optioncan be used, which will mean that theunchecked_...methods can become const, andconst_mut_refscan be used.parse_str_radixis not necessary now, given it can be achieved withU::from_str_radix(s, r).unwrap(). So I'm thinking of removing it.Use the error traits from
core::numCurrently, bnum defines its own error types
ParseIntErrorandTryFromIntError, which are used for the same purpose as the error types with the same names fromcore::num. This is because those errors fromcore::numcan't be initialised via something like anewmethod and their fields are private. However, it is possible to create them by deliberately invoking an error, e.g.So a possible design change is to use these error types from
core::numrather than bnum's own ones. But maybe that way of creating them seems a bit hacky/bad practice.Remove implementations of e.g.
From<u128>for nowSince it is currently not possible (on stable) to write trait implementations only for certain range of values of a const-generic parameter, the existence of implementations of
From<n>for primitive integersnis not quite correct, since the number of bits of the bnum could be smaller than the number of bits ofn. So a possible change is to remove these implementations and only implementTryFrom<n>instead (and hopefully conditional trait impls based on const-generics are added to stable Rust relatively soon). Conversions that don't return aResultwould still be possible via theCastFromandAstraits.Put some functionality behind crate features
To reduce compile times, the functionality for signed integers, as well as functionality that requires the
alloccrate, could be put behind crate features (called e.g.signed-intsandalloc) which would be enabled by default. In particular, ifallocfunctionality was optional, then bnum could be used in environments with no global allocator.Remove the
Add<Digit>implIt doesn't really make sense to have this and not
Add<u>for other primitive integersu, and I think having all of those would be messy.Arbitrary fixed-width floats
This would be a huge new feature, but I've left it to the end as it's obviously separate from all the integer-related changes. I've been working on the functionality for floats on and off for some time, and basically all of the methods from
f32andf64are there apart from the mathematical functions (e.g.exp,log,sin) and the mathematical constants (e.g.PI,E). I'm not sure how much this would be used, as the software implementations would obviously be quite a bit slower thanf32andf64, but it would be interesting to see how many people would like to have this added.Other things
Any other comments or suggestions are welcome! Thank you for reading and looking forward to hearing your thoughts!
Beta Was this translation helpful? Give feedback.
All reactions