-
Notifications
You must be signed in to change notification settings - Fork 186
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 want arithmetic ops which return error instead of losing precision (was: I want arithmetic ops which never lose precision) #515
Comments
In this case this is the wrong library for you. An arbitrary precision numeric library is what you want - but you need to be careful. Some numbers (even rational numbers) cannot be represented in a finite string of decimal digits (e.g. Therefore, the conclusion is: if you're going to lose precision anyway, what you really need is to restrict the amount of precision loss and make sure your loss is small enough to be tolerated. Just to reemphasize: THERE IS NO "NEVER LOSE PRECISION". It is impossible. You can only get: Lose very very very little precision, so small that it doesn't matter to anybody. |
@schungx . I want fixed precision library, not infinite precision one.
I think I was clear enough in bug description. My application has zero tolerance to precision loss. My application deals with money. Its only purpose is to verify that two given monetary amounts are absolutely equal. But I can tolerate panics, assertion faults, etc. They are okey. If I will encounter panic, I will simply dig into data and try to understand why the panic happened and try to do something. Panics are better that silent precision loss I will never know about |
Again: I want function, which tries to multiply values. When I give |
In other words, you want methods to fail on overflow or underflow, not rounding. I suppose the This is pub fn checked_mul(self, other: Decimal) -> Option<Decimal> {
match ops::mul_impl(&self, &other) {
CalculationResult::Ok(result) => Some(result),
CalculationResult::Overflow => None,
_ => None,
}
} As you can see, it is supposed to return |
@schungx . As I said earlier fn main() {
use rust_decimal::Decimal;
let a: Decimal = Decimal::from_str_exact("1.0000000000000000000000000001").unwrap();
let b = a.checked_mul(a);
let c = b.unwrap(); // XXX
assert_eq!(c, Decimal::from_str_exact("1.0000000000000000000000000002").unwrap());
} This code passes without panic (I use rust_decimal 1.23.1). But I want this code to panic at line marked with XXX |
Just a hunch: Technically, you may actually want |
@schungx . No, |
No it isn't. It is complicated. Let me explain: 2^96 = 79228162514264337593543950336 You can see MAX-96-bits has 29 decimal digits. However, the first digit can no more than 7, therefore it is missing 8 and 9. Therefore 96 bits can at most represent 28 digits fully, or 29 digits partially. Notice the word "partially". It means that a 29-digit number is inexact. You just happen to get the same display. When you do arithmetic calculations, especially multiplication, that inexactness at the 29th digit will start accumulating errors. Try your test with only 28 digits and see... |
Okey, I removed one zero. Result is same. I. e. the following code passes to end instead of panicking. fn main() {
use rust_decimal::Decimal;
let a: Decimal = Decimal::from_str_exact("1.000000000000000000000000001").unwrap();
let b = a.checked_mul(a);
let c = b.unwrap();
assert_eq!(c, Decimal::from_str_exact("1.000000000000000000000000002").unwrap());
} |
Well there goes the idea then. Nevertheless, Maybe @paupino can take a look at this, as the |
This is actually the same issue as #511 which got raised the other day too. Effectively, what we're talking about here is underflow handling. By default, Furthermore, multiplication is a bit different to other operations. While it's true that we reserve 96 bits for the mantissa representation within a standard decimal, we effectively reserve 192 bits for the product. This is because multiplication can naturally increase the scale - e.g. The There are a couple of ways for going about this. The first is to modify the bitwise functions to make underflow handling optional. This could then be exposed either via a feature flag or an explicit function. My concern with this approach is that it is very limited in scope - it's relatively easy to underflow, and sometimes in ways that have no meaningful difference (e.g. The second is to provide "delayed boxing" functionality. That is, keeping the number in it's raw Anyway, all this to say - there isn't a "quick fix" to this right now - it's currently working by design. It's on my todo list to take a look at the fundamentals of I will go on to say that |
Make sense! Underflow means very very small errors (errors that are smaller than the minimum number representable by this format) and in most usage you'd want to ignore it (therefore rounding it up). However, it may disrupt the meaning of |
@paupino
You mean https://docs.rs/bigdecimal ? I think it is slow, I don't like this. Moreover, it seems to be badly designed, I wonder is there at least one use case, where bigdecimal will be actually useful. Let me show you example of bad design. Consider this pseudo code: // Pseudo code! Uses bigdecimal
let a = from_str("0.5") + from_str("0.5");
loop {
let b = a * a;
a = b;
} This code should loop forever and every iteration should be fast (because we simply do 1 * 1 at every iteration). But in fact every iteration turns to be 2x slower than previous. (I will report this to upstream tomorrow with actual code example.) |
So I will go writing my lib. You may close the bug |
Yep, that's the one I was referring to!
Adding this feature is definitely on the roadmap as it has come up a few times however when is still open for discussion. I'd like to take a look at this (and surrounding issues) this month however it all depends on how my work schedule pans out! I'll keep this issue open for the meantime as it helps me also group by demand. If you did want to have a go at adding the feature instead of writing a new lib then then branching logic for https://github.com/paupino/rust-decimal/blob/master/src/ops/mul.rs#L132 The rescale function that does the actual rescaling/rounding is here: https://github.com/paupino/rust-decimal/blob/master/src/ops/common.rs#L337 Either way: good luck and thanks for creating an issue! |
I reported bug I mentioned: akubera/bigdecimal-rs#86 |
Duplicate of #511. We'll keep the other one open for now, as this one seems to be resolved by just switching to |
I use rust-decimal for monetary calculations. rust-decimal sometimes lose precision, and I don't like that. For example,
1.0000000000000000000000000001 * 1.0000000000000000000000000001
is1.0000000000000000000000000002
under rust-decimal (this is incorrect from mathematical point of view). (I used 1.23.1.) Evenchecked_mul
gives same answer instead of returning error. I want some function (say,exact_mul
and similar names for addition and subtraction), which returns Ok (or Some) when a result can be exactly represented and Err (or None) if not.In my program (assuming my program is bug-free) such precision losing should never happen. But as we all know we can never be sure that there is no bugs! So I want this
exact_mul
. I would replace mychecked_mul
withexact_mul
(withunwrap
). And if I ever lose precision, I will discover this, because I will see panicThe text was updated successfully, but these errors were encountered: