0

I have this Yul function that I am trying to reverse to Solidity for learning purposes. My results with the solidity function are different than the original functions and I don't see what I am missing.

Initial Yul function:

    function fpow(
        uint256 x,
        uint256 n,
        uint256 baseUnit
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := baseUnit
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store baseUnit in z for now.
                    z := baseUnit
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, baseUnit)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, baseUnit)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, baseUnit)
                    }
                }
            }
        }
    }

My Solidity function:

    function fpowSol(
        uint256 x,
        uint256 n,
        uint256 baseUnit
    ) public pure returns (uint256 z) {
        if(x == 0) {
            if(n == 0) {
                return (baseUnit);
            } else {
                return (0);
            }
        }

        if(n % 2 == 0) {
            z = baseUnit;
        } else {
            z = x;
        }

        uint256 half = baseUnit >> 1;

        for(n >>= 1; n > 0; n >>= 1) {
            uint256 xx = x * x;
            if(xx / x != x) { require(false, "xx overvlowed"); }

            uint256 xxRound = xx + half;
            if(xxRound < xx) { require(false, "xxRound overflowed"); }

            x = xxRound / baseUnit;

            if(n % 2 == 0) {
                uint256 zx = z * x;
                require(zx / x == z, "zx overflowed");

                uint256 zxRound = zx + half;
                if(zxRound < zx) { require(false, "zxRound overflowed"); }

                z = zxRound / baseUnit;
            }
        }
        return (z);
    }

How would you rewrite this Yul function in solidity

or

Where am I wrong in my translation from Yul to solidity ?

1 Answer 1

0

The reason why I wasn't able to get the same result is because of the last if statement which was misleading to me

                    // If n is even:
                    if mod(n, 2) { ... }

Actually this is not if(n%0 != 0) and is in reality "if n is not even".

I'm new to yul so correct me if I'm wrong but I have been able to get consistent results to the main function and also to my calculation off chain. I assume this answer is correct.

Real function is

    function fpowSol(
        uint256 x,
        uint256 n,
        uint256 baseUnit
    ) public pure returns (uint256 z) {
        if(x == 0) {
            if(n == 0) {
                return (baseUnit);
            } else {
                return (0);
            }
        }

        if(n % 2 == 0) {
            z = baseUnit;
        } else {
            z = x;
        }

        uint256 half = baseUnit >> 1;

        for(n >>= 1; n > 0; n >>= 1) {
            uint256 xx = x * x;
            if(xx / x != x) { require(false, "xx overvlowed"); }

            uint256 xxRound = xx + half;
            if(xxRound < xx) { require(false, "xxRound overflowed"); }

            x = xxRound / baseUnit;

            if(n % 2 != 0) { // THIS HAS CHANGED
                uint256 zx = z * x;
                require(zx / x == z, "zx overflowed");

                uint256 zxRound = zx + half;
                if(zxRound < zx) { require(false, "zxRound overflowed"); }

                z = zxRound / baseUnit;
            }
        }
        return (z);
    }

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.