paupino / rust-decimal

Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.
https://docs.rs/rust_decimal/
MIT License
980 stars 179 forks source link

Unsigned Decimal? #664

Open WestXu opened 1 month ago

WestXu commented 1 month ago

It's a bit cumbersome to validate the signs in every function where sign is meaningless.

Is it considered something useful to add to this crate? And what's the best practice for creating one from the user side?

paupino commented 1 month ago

This is an interesting use case. It's certainly possible - effectively a Decimal is a 96 bit unsigned mantissa with the only thing denoting it as a negative is a single bit in the flags. Making it unsigned would be effectively ensuring that the flag never gets set - if it does, then it'd be invalid.

Can you perhaps share your use case a bit more?

WestXu commented 1 month ago

Sure. In the context of high freq trading, there are 2 ways to define a trade:

// currently
struct Trade {
    price: Decimal,
    quantity: Decimal, // sign indicates the direction, negative means sell
}
// ideally, UDecimal is unsigned
enum Side {
    Buy,
    Sell,
}
struct Trade {
    price: UDecimal,
    quantity: UDecimal,
    Side: Side,
}

The ideal definition rules out the undefined behavior of negative price, and prevents confusion of whether quantity is signed.

So my question is can we add UDecimal to the crate, or if I want to impl myself in my application code, what would be the best practice? nutype?

paupino commented 1 month ago

Adding UDecimal is possible however I think it would be a minor refactor. I think it would effectively need to be it's own struct which implements some of the some traits that the current Decimal does. It's obviously more nuanced - as an example, it would happily do From<u32> however would only be able to provide TryFrom<i32> whereas it can safely provide From<i32> in the current implementation.

The new type pattern in your application logic may be a little easier right now, only because you'd only need to (re)implement the traits/functions that you care about. If implementing directly into the library (which can be done if you have the appetite) it'd need to handle quite a lot of cases (e.g. enable the U variant, consider how to handle it with db access etc).

I'm not familiar with that crate, however if it can provide transparent forwarding then it may be a quick and easy way to implement this, so long as you can intercept and assert non-negative values.

ldicarlo commented 1 month ago

Hey! Thank you for this crate, I just wanted to say that I came here exactly for this issue, and also for an Unsigned Strictly Positive Decimal. I would love to not have to validate non-zero numbers in my APIs. (As suggested in the famous article parse, don't validate)