Solidity

如何取兩個uint256的平均值而不溢出?

  • March 31, 2021

假設您有兩個 uint256 數字x, 和y。計算它們之間的算術平均值的一種方法是:

function avg(uint256 x, uint256 y) external returns (uint256 result) {
   result = (x + y) / 2;
}

但是當中間結果x + y不適合 uint256 時,這不起作用。

一個看似更好的方法如下:

function avg(uint256 x, uint256 y) external returns (uint256 result) {
   result = x / 2 + y / 2;
}

x這不會溢出,但是當兩者和y都是奇數時它不起作用- 0.5 餘數失去兩次。x實際上,如果您希望對結果進行四捨五入,則如果其中一個或y是奇數,這將不起作用。

如何在 Solidity 中取兩個 uint256 數字的平均值而不溢出?

發布問題後,我意識到我自己給出了答案。

我只需要檢查xy是否都是奇數,如果是,則在結果中加 1:

function avg(uint256 x, uint256 y) external returns (uint256 result) {
   result = x / 2 + y / 2;
   if (x % 2 == 1 && y % 2 == 1) {
       result += 1;
   }
}

更新:我最終優化了這個功能,因為我發現它相當昂貴,大約 210 gas。我將程式碼包裝在一個unchecked塊中,使用按位運算符 AND 而不是 MOD 運算,最後應用了德摩根定律:

function avg(uint256 x, uint256 y) external view returns (uint256 result) {
   unchecked {
       result = (x >> 1) + (y >> 1);
       if (!(x & 1 == 0 || y & 1 == 0)) {
           result += 1;
       }
   }
}

這將成本降低到~82 gas。

第二次更新:我設法通過擺脫 if 塊來節省更多的氣體:

function avg(uint256 x, uint256 y) external view returns (uint256 result) {
   unchecked {
       result = (x >> 1) + (y >> 1) + (x & y & 1);
   }
}

這將天然氣成本降低到約 60。

引用自:https://ethereum.stackexchange.com/questions/96381