I’ve been looking for a way to emulate full precision a * b / c
by using modulo math. It is possible to do by emulating a wider integer by storing it as 2x uint64:
- Math in Solidity (Part 3: Percents and Proportions) | by Mikhail Vladimirov | Coinmonks | Medium
- Mathemagic: Full Multiplication. How to do multiplication with full 512… | by Remco Bloemen | wicketh | Medium
- Mathemagic: 512-bit Division. In this post I will introduce five… | by Remco Bloemen | wicketh | Medium
Problem for our ScriptVM is that we don’t have uint64, so can’t cleanly split an uint128 to 2x uint64. Maybe we could implement it with uint63 & emulated uint126 as the uint63 can fit into a ScriptNumber, and uint126 can fit into 2 ScriptNumbers.
Division part is the big problem, it needs a bounded loop - worst case it would need 63 iterations. With higher limits CHIP the loop could be unrolled, and implementing it with Script would be quite expensive, lots of bytecode just to perform a single muldiv
- an operation we can expect will be common.
If int32
precision is ok, then contracts can use the relatively small macro (OP_ROT OP_ROT OP_MUL OP_SWAP OP_DIV
).
If more precision is needed, contracts must compromise on accuracy and input range. Latest example of this is fex cash MULDIV implementation.
I think this demonstrates why OP_MULDIV
would be very valuable.
It is cheap and easy to implement the opcode, I already implemented the muldiv
function in C for another purpose (multiplying a value with a small factor to calculate blocksize limit algo), the OP_MULDIV
would simply be doing this:
uint64_t muldiv(uint64_t x, uint64_t y, uint64_t z) {
uint128_t res;
res = ((uint128_t) x * y) / z;
assert(res < (uint128_t) UINT64_MAX);
return (uint64_t) res;
}
For VM purposes, it would be operating on signed ints, and should fail on result overflow or on z=0, to be consistent with OP_MUL
and OP_DIV
.