4

I'm a bit lost on what to do with the following calculation. The problem is that decimals are being round up/down, which obviously doesn't give me the result I desire.

int totalValue = 275000;
int percentage = 2;

for (uint i = 0; i < 30; i++) {
  totalValue += totalValue * percentage / 100;
}

With the above code, the result is:

498108

The expected result is:

498124.436

I know that normally you could simply use the following calculation to work with a predetermined amount of decimals like so:

value * 10**4 (4 decimals)

But I haven't gotten that to work in this case, often resulting in insanely high numbers due to the multiplication and division that "do not make sense" once I revert the above calculation using:

value / 10**4

Thank you very much for your time. Any help would be very much appreciated!

4

You need to multiply before calculations and divide after that:

int decimals = 4;
int totalValue = 275000;
int percentage = 2;

totalValue = totalValue * 10**decimals;
for (uint i = 0; i < 30; i++) {
  int AddValue = totalValue * percentage;
  AddValue = AddValue / 100;
  totalValue += AddValue;
}
totalValue = totalValue / 10**decimals;

EDIT: fixed line 5 and the percentage calculation EDIT2: Actually you don't need to multiply the percentage and will get better results - fixed

  • Thank you for your answer! I was working on a solution that was similar to this, but I can see where I went wrong in several places. On line 5 I think it's missing an assignment, right? Also I keep getting "275000" as output with this example, but I haven't figured out why – Nick May 20 at 13:49
  • Wont the line "percentage = percentage / (100 * 10**decimals);" also get truncated to 0? – Nick May 20 at 13:55
  • 1
    made several errors in a hurry ... should be fixed now – KNK May 20 at 14:39
  • Awesome, that seems to work! I'll validate it properly as soon as possible. One more question though. This function will still return a rounded number, right? So would it be better to take out the last line and account for the fact that the last 4 digits are decimals wherever I'm calling the backend from? -or am I doing something wrong? – Nick May 20 at 14:47
  • 1
    There are no floating point numbers in Solidity (yet), so the number as always rounded at the end, but yes you can return the number as is and divide in the client to get a floating point result – KNK May 20 at 14:58
7

Generally speaking, in order to avoid data-loss (i.e., precision-loss) in an arithmetic computation, try to postpone every division as much as possible, without altering the expression.

More specifically with regards to your code, it appears that you are trying to compute the result of applying compound interest: 275000 * 1.02 ** 30.

You can do this very precisely in Solidity, by calculating (off-line) the exact value of 1.02 ** 30, and storing it scaled up to the level of precision that you desire.

Moreover, you won't even need those 30 iterations in order to achieve all that, so you can optimize your code here for both accuracy and performance.

For example:

uint256 INTEREST_N = 181136158410335375505681049921897;
uint256 INTEREST_D =  10000000000000000000000000000000;
uint256 totalValue =  275000 * INTEREST_N / INTEREST_D;

Of course, I'm aware of the fact that you most likely want the 275000 part as an input from the outside rather than a hard-coded value (in which case, you could just as well compute everything outside of the contract).

But assuming that the number of iterations and the compound interest percentage are both constant (30 and 2% in your case), you can do it using the numerator and denominator described above.

If either the number of iterations or the compound interest percentage is a variable, then instead of a single pair of numerator and denominator you can use an array of numerators and an array of denominators.

If both the number of iterations and the compound interest percentage are variables, then instead of a pair of arrays you can use a pair of tables.

  • I appreciate the answer and understand the gist of it, thank you! I'll come back to this solution once my understanding of the issue has improved. – Nick May 22 at 7:37
  • 1
    @Nick: NP. In your coding example, you could have simply used a constant (or two constants), so I suppose that this example does not represent what you're actually trying to achieve. As explained in my answer, I tend to guess that your intention is for one or more of these constants values (275000, 30 and 2%) to be an input variable. If you provide a description of the actual purpose, then I might be able to guide you through and suggest a scheme which will allow you to realize it while optimizing for both accuracy and performance (i.e., gas cost). – goodvibration May 22 at 7:42
  • That's awesome! In my case all three of those values are variables, so I figured I might as well send the result of 1.02**30 from the backend/caller. Also, the value of 275000 can contain decimals. At this point I'm not too sure how that will affect the calculation, but added "100000000000000000000000000000000 / (10**decimals);" to make the amount of decimals in the outcome variable. – Nick May 22 at 8:01
  • To add a more complete description of the variables with examples: the initial value (275000.5115 , max 4 decimals), interest rate (2.2152%, max 4 decimals) and amount of years (30, always a round number). To make it easier I can make sure there's always 4 decimals by padding zeroes. – Nick May 22 at 8:07
  • @Nick: Sorry, but in that case you can do the full calculation offline and just put the result hard-coded in your contract. There is no need for any code apart from that. – goodvibration May 22 at 9:37
2

The reason you are getting the value 498108 is because Solidity truncates divided values.

This means that if you divide 3/2, the result will be 1, not 1.5. Similarly, if you divide 11/3, the result will be 3, not 3.666.

With this in mind, the reason you are getting that final result is because a few of the loop results are being truncated.

  • So you're saying that it's not possible in any way? I'm aware of what's happening (actually I thought it was rounding it down/up, but you're saying it's truncating. Thanks for letting me know!), but I don't know how else to approach this to get the result I want. – Nick May 20 at 13:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.