Skip to main content

Safe Arithmetic

Description

Solidity performs integer division by truncation, which can result in loss of precision. This can happen when division is performed before multiplication, as the result of the division may not be precise enough to maintain the desired level of precision in the final result. In some cases, performing multiplication before division can help avoid this issue and ensure that the result is as precise as possible.

For example, imagine a smart contract that splits a balance among a group of addresses. If the division is performed before the multiplication, the result may not be precise enough to ensure that each address gets the correct share. However, if the multiplication is performed first, the result will be more precise and each address will receive the correct share.

Example Code

function splitBalance(uint256 balance) public {
uint256 numAddresses = 5;
uint256 someMultiplier = 12;
uint256 share = balance / numAddresses * someMultiplier;
uint256 remainder = (balance % numAddresses) * someMultiplier;

for (uint256 i = 0; i < numAddresses; i++) {
uint256 amountToSend;
if (i == 0) {
amountToSend = share + remainder;
} else {
amountToSend = share;
}
address payable recipient = addresses[i];
recipient.transfer(amountToSend);
}
}

In the example above, the splitBalance function is designed to split a balance among a group of five addresses. However, because the division is performed before the multiplication, there is a risk of precision loss. For example, if the balance is 19, and the division is performed first, each address would receive a share of 3, with a remainder of 4 so each address will get 312=36 tokens and the first address will get 312 + 412 = 84 tokens. However, if the multiplication is performed first, each address would receive a share of 1912/5=45.

Recommendation

To avoid precision loss when working with integer division in Solidity, it's generally recommended to perform multiplication before division. By doing so, you can ensure that the result is as precise as possible and that each value is accurately represented in non-floating data types.

Here is an example of the splitBalance function with multiplication performed before division:

function splitBalance(uint256 balance) public {
uint256 numAddresses = 5;
uint256 someMultiplier = 12;
uint256 share = balance * someMultiplier / numAddresses;
uint256 remainder = balance * someMultiplier % numAddresses;

for (uint256 i = 0; i < numAddresses; i++) {
uint256 amountToSend;
if (i == 0) {
amountToSend = share + remainder;
} else {
amountToSend = share;
}
address payable recipient = addresses[i];
recipient.transfer(amountToSend);
}
}

In the example above, multiplication is performed before division by multiplying balance by (someMultiplier) before dividing by numAddresses. This helps ensure that the result is as precise as possible and that each address receives the correct share.