0

In my rust code when I make divisions an extra number is added in the decimal but in some cases it does not.

For example, in the code below if 500 is divided by 10^24 it yields: 0.000000000000000000000500000000000000005 which when converted back(500 * (10^24)) I get 500.00000000000006. but for other numbers like if amount is 700 I get the correct answer.

let amount = 500;
let base = 10;
let result = amount as f64 / base.pow(24) as f64;

Is there something I'm doing wrong here or is there a better way of doing this?

Starbody
  • 153
  • 2
  • 12
  • 1
    Not all floating point divisions are exact (actually most), sometimes even when they're not exact, multiplying with the divisor still takes the result back to the original, sometimes not. It's just how it is. Maybe there is a better way to do "this", but what exactly is "this", what's the context and what properties does the result have to have? – harold Sep 08 '22 at 21:28
  • I'm trying to build a financial application and need the calculations to be exact. I'm having concerns that the extra decimal may affect the overall result of the calculation since the amount may change based on user input – Starbody Sep 08 '22 at 21:31
  • I would prefer the value is something like 499.9999 when converted back rather than 500.00000000000006 – Starbody Sep 08 '22 at 21:34
  • 1
    Is the divisor always a power of ten? In that case you can use a decimal type (there are some crates), which is exact for divisions by a power of ten (just as binary floats are exact for divisions by a power of two), though still not for division by arbitrary numbers. – harold Sep 08 '22 at 21:35
  • the divisor will always be 10 to the power of 24 – Starbody Sep 08 '22 at 21:37
  • Yeah! it does help. I'll have to truncate the decimal to a reasonable decimal place – Starbody Sep 08 '22 at 21:50
  • I seriously recommend against trying to truncate to some number of decimal places. f64 doesn't have decimal places, not really. Even when it works, that will just hide the fact that the calculation has fundamental problems, creating misplaced confidence. It would be extremely difficult to show that it *always* works. Decimal floats (again, see a crate) would solve the problem reliably. – harold Sep 08 '22 at 22:05
  • 2
    For some financial applications, there's a simple workaround: just use integers to keep track of the number of _cents_ and not _dollars_. But if you're doing something a little more complicated like calculating interest, this won't solve your problem since you'd need fractional parts of cents. – Andrew Sep 08 '22 at 22:08
  • 1
    Seconding both these last comments. The issue you're running into is exactly why using floating-point for financial applications is considered a bad idea. – loganfsmyth Sep 08 '22 at 22:58
  • Thanks for pointing me in the right direction. I am trying the `rust_decimal` crate. Here is what I'm trying out `Decimal::from_f64(amount as f64 / BASE.pow(TOKEN_DECIMAL as u32) as f64).unwrap().to_f64()` – Starbody Sep 09 '22 at 00:37
  • but what if I want to do an arithmetic operation on that value(the decimal value) like raising it the power of another number, do I have to convert it to a float again? – Starbody Sep 09 '22 at 00:51
  • The operations, including the division, should happen on decimals. That's the point. Doing operations on f64 and then converting to decimal, computes a bad number and then converts it to decimal, what's the point? – harold Sep 09 '22 at 10:56
  • But I want to do some operations that require raising numbers to the power of another and the `rust_decimal` crate does not have a power function. How do I then raise the decimal to the power of another number(maybe another decimal)? – Starbody Sep 09 '22 at 11:33
  • It depends. 10^24 could be created from a string or through `from_i128_with_scale` for example. Or the division could be performed without division by using the same function, as long as the input number could be represented as an `i128` (could be an integer number of *cents*, and a scale of 26 instead of 24). But raising arbitrary numbers to arbitrary powers is a problem. – harold Sep 09 '22 at 12:28
  • My calculations have something to do with raising a number to the power of `1/3`(which is a constant). let's say I represent all numbers in cents. how do I do the `n^(1/3)`? With `from_i128_with_scale` how can I raise an arbitrary number(converted to cents) to the power of a constant? – Starbody Sep 09 '22 at 13:21
  • That's getting serious. Putting aside 1/3 itself, taking the cube root of a number typically results in an irrational number, so there is pretty much no way to exactly represent the result at all (there is but that's getting into highly obscure techniques). Is there some standardized specification of how this calculation should happen in a finance context? – harold Sep 09 '22 at 13:32
  • Not really. `1/3` is a ratio derived from another calculation. I think the only feasible solution would be to do the calculation and then round it down to a tolerable decimal value. Since the issue with the decimal usually is something like `.0000000000000007` the effect of this precision problem will not result in too much change in the digits part since the main point of interest to users is the digits. – Starbody Sep 09 '22 at 13:54

0 Answers0