Solidity
如何取兩個uint256的平均值而不溢出?
假設您有兩個 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 數字的平均值而不溢出?
發布問題後,我意識到我自己給出了答案。
我只需要檢查
x
和y
是否都是奇數,如果是,則在結果中加 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。