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;
}