-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Due to using fixed-point number representation, operators that require multiplying some inputs would naturally increase the scale of the output, so we usually perform post-rescale, which consists on dividing the output by a power of the scale, depending on how many operands were multiplied together.
An other point to consider is that, when multiplying two inputs, the output's bit length equals the sum of inputs bit-length.
Hence multiplying two > 16bits i32 values yields a number outside of i32, causing overflow.
When dealing with fixed-scale numbers, we artificially increase the bit-length. As an example, 100 with fixed-point representation and 7 bits of precision would be mapped to element 100 * 2^7 which has bit-length 13, where 100 has bitlength 6.
The post-rescaling and increased bit-length cause those operations to overflow in our virtual machine, where it should not.
One mitigation currently used is to instead pre-rescale, which means divide by the scale BEFORE computing the operation, but this can lead to severe error.
One better way to mitigate this, I believe, would be to perform the operation "inside" the division sumcheck.
Current workflow (Mul)
1st step, Mul:
tmp(r) = ∑ eq(r, x) · In0(x) · In1(x)
2nd step, rescale:
0 = ∑ eq(r, x) · ( out(x) · S + r(x) - tmp(x) ) | Where S is the scale factor, r the remainder polynomial.
Limitation is that tmp is cast to i32, where it would oveflow.
Proposed workflow (Mul)
One only step:
0 = ∑ eq(r, x) · ( out(x) · S + r(x) - In0(x) · In1(x) )
This allows to only use field elements for the inner operations, which can hold really big bit-length, and then only the correctly scaled output is cast to i32.