13

I have seen this question here about representing decimals in solidity, and although of course the answer is that 'msg.value' is always in Wei, this got me thinking about how Ethereum handles division of odd or prime numbers.

For example if I have 481129598370820487 Wei and I need to distribute that amongst several parties, I am going to have a problem.

In addition the Ethereum Converter seems to give up trying to convert 0.481129598370820487 ether into anything, but I'm guessing this is the limitation of front end javascript.

So how does Ethereum solve this?

3 Answers 3

22

Summary

  • The prime number being divided is just a standard integer division. The developer will have to decide how to handle the remainder.
  • The Ethereum Converter calculation is correct. Loading the "prime" number from the provided URL will not trigger the JavaScript calls to perform the calculations. But if you paste the number into the webpage, the calculations will work.
  • The Ethereum software suite would each have to handle integer division appropriately for each case.
  • BONUS ANSWER. Division by 0 in Solidity will result in 0, not an exception. The Smart Contract developer should check for the denominator being zero and handle the case appropriately. eth has provided the link Why does a Solidity throw consume all gas? for the explanation.

    UPDATE : As at Solidity version 0.4.15, division by 0 throws a Runtime Error: http://solidity.readthedocs.io/en/v0.4.15/types.html#integers


Details below.



Standard Integer Division

Handling the prime number in your scenario above is just the same a working with integer divisions. The division would result in a floor of the calculation with the remainder discarded. Following is a simpler prime number being divided:

17 / 3 = 5, with the remainder of 2 discarded


Distributing A Prime Among Different Parties

When performing this calculation, I would firstly divide the number proportionally among the parties. After this first round of calculations, I would sum the total and subtract this number from the original amount - I will have a remainder representing the leftover. I would then allocate this remainder as proportionally as I can.

So for example, you are a pool and you state that you distribute all earnings, and there are 6 miners (parties) equally contributing their hashrates.

Using the following Java code:

BigInteger prime = new BigInteger("481129598370820487");
System.out.println("prime                 = " + prime);
BigInteger parties = new BigInteger("6");
System.out.println("parties               = " + parties);
BigInteger sharePerParty = prime.divide(parties);
System.out.println("sharePerParty         = " + sharePerParty);
BigInteger totalSharesFirstRound = sharePerParty.multiply(parties);
System.out.println("totalSharesFirstRound = " + totalSharesFirstRound);
BigInteger remainder = prime.subtract(totalSharesFirstRound);
System.out.println("remainder             = " + remainder);

// Output below
prime                 = 481129598370820487
parties               = 6
sharePerParty         = 80188266395136747
totalSharesFirstRound = 481129598370820482
remainder             = 5

If it is more important that the pool distribute ALL earnings then:

  • 5 parties should receive 80188266395136747 + 1
  • 1 party should receive 80188266395136747

If it is more important that the parties all receive the same share then:

  • 6 parties should receive 80188266395136747
  • The pool gets to keep the 5 and profit!


Ethereum Converter

Here is the main calculation code for the Ethereum Converter - ether.converter.js. If you copy your number 0.481129598370820487, navigate to http://ether.fund/tool/converter and paste in your number, the figures are calculated as expected. There is nothing wrong with the handling of prime numbers in the Ethereum Converter.

Using the link that you provided does not work as the number in the URL (#v=0.481129598370820487&u=ether) does not trigger the JavaScript conversion calculation.


How Ethereum does it

From my quick look at the geth source code and guesswork (+ many years working with floating point and integer calculations), Ethereum (the software) would be using integer division throughout the code - remainders are just discarded.

The Etereum VM has the DIV (unsigned integer division) and SDIV (signed integer division) instructions. These are implemented in core/vm/instructions.go - lines 136 to 163 and has the following code which just does standard integer divisions.

func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
    x, y := stack.pop(), stack.pop()
    if y.Cmp(common.Big0) != 0 {
            stack.push(U256(x.Div(x, y)))
    } else {
            stack.push(new(big.Int))
    }
}

func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
    x, y := S256(stack.pop()), S256(stack.pop())
    if y.Cmp(common.Big0) == 0 {
            stack.push(new(big.Int))
            return
    } else {
            n := new(big.Int)
            if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
                    n.SetInt64(-1)
            } else {
                    n.SetInt64(1)
            }

            res := x.Div(x.Abs(x), y.Abs(y))
            res.Mul(res, n)

            stack.push(U256(res))
    }
}

I now test out what happens in the execution of smart contracts when a number is divided by zero, as the following result is returned:

            stack.push(new(big.Int))



What is the result of a divide by 0 in a smart contact

Using the following source code

contract DivZero {
  function divide(uint numerator, uint denominator) public constant returns(uint) {
    return numerator / denominator;   
  }
}

Stripping out the CR LF and running the following code in geth --dev:

var divzeroSource="contract DivZero { function divide(uint numerator, uint denominator) public constant returns(uint) { return numerator / denominator;  }}"

var divzeroCompiled = web3.eth.compile.solidity(divzeroSource)

var divzeroContract = web3.eth.contract(divzeroCompiled.DivZero.info.abiDefinition);

var divzero = divzeroContract.new(_numerator, _denominator, {
  from: eth.accounts[0], 
  data: divzeroCompiled.DivZero.code, gas: 4000000}, 
  function(e, contract) {
    if(!e) {
      if(!contract.address) {
        console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
      } else {
        console.log("Contract mined! Address: " + contract.address);
        console.log(contract);
      }
    } 
  });

Then running the following statements in geth:

> divzero.divide(4123, 7)
589
> divzero.divide(4123, 0)
0

Conclusion. Divide by zero in Solidity does not throw an error but just returns the result of 0.

So in your code, it is better to check for a denominator of 0 before a division operation

if (denominator == 0) {
  // Handle error condition
} else {
  result = numerator / denominator;
}
2
  • 3
    In Ethereum, division by 0 is 0 because there's essentially only 1 type of exception in the EVM: Out of Gas ethereum.stackexchange.com/a/2548/42
    – eth
    Commented Apr 15, 2016 at 4:21
  • Thx for clarifying eth. Commented Apr 15, 2016 at 4:26
14

I know the answer for odd numbers and I am guessing the same would hold true for prime numbers.

Let's say you want to divide 501 wei equally between 2 parties: 501/2 = 250. Each address would be sent 250 wei, leaving 1 wei in the contract.

This is known as integer division meaning "The result is the integer quotient of number1 divided by number2. The integer quotient discards any remainder and retains only the integer portion."

Related: Dividing Ether Past Smallest Unit Possible?

1
  • In my opinion this would be a better answer if you mentioned that's called integer division
    – cat
    Commented Apr 14, 2016 at 23:28
1
//Use this to test your division problems
pragma solidity ^0.4.23;
contract Ctrt {
    uint public numerator;
    uint public denominator;
    uint public quotient;
    uint public remainder;
    constructor() public {
      numerator = 100;
      denominator = 3;
    }
    function setNumbers(uint _numerator, uint _denominator) public {
      numerator = _numerator;
      denominator = _denominator;
    }
    function safeDiv(uint _numerator, uint _denominator) public {
      quotient = _numerator / _denominator;
      remainder = _numerator - quotient * _denominator;
    }
    function safeDivDefault() public {
      quotient = _numerator / _denominator;
      remainder = _numerator - quotient * _denominator;
      //result: 100/ 3 => quotient 33 + remainder 1
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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