解决求平均值出现加和导致的溢出问题
解決求平均值出現加和導致的溢出問題
目錄
- 解決求平均值出現加和導致的溢出問題
- 方法一
- 方法二
- 方法三
- 方法四
- 方法五
試問求平均值能玩出什么樣的花樣來?
微軟大神@Raymond發表了一篇長文,講述的是關于求平均值的算法。
附上長文鏈接:https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223#comments
求平均值這很簡單,小學時候的知識,無非就是N個數的加和在除以N,再簡單不過了。
對于無符號的兩個數求平均,我們通常這么寫:
(注:這里uint32_t以32位表示)
uint32_t average(uint32_t a, uint32_t b) {return (a + b) / 2; }但是這樣就存在一個問題,當a和b兩個值都比較大的時候,a加b的結果會超出uint32_t的最大表示范圍,例如:
!!!!!!!!!average(0X80000000, 0X80000000) = 0!!!!!!!!!!!!!
所以為了避免這種情況,進行優化成這樣:
方法一
uint32_t average(uint32_t low, uint32_t high) {return low + (high - low) / 2; }此方法需要知道大的那個值,當知道相加的兩個無符號整數中的較大值時,減去較小值再除二,就可以以提前減少長度,從而避免了數值溢出。
方法二
還有另一種算法不依賴于知道哪個值更大,已經被申請為美國專利于2016年到期:
uint32_t average(uint32_t a, uint32_t b) {return (a / 2) + (b / 2) + (a & b & 1); }這個方法的訣竅在于將兩個數先進行除法縮小,然后同時對最低位進行位與,從而保證當兩個數都為奇數時,(a & b & 1)的結果能得到前面除法的一個補正。
方法三
該方法被稱為SWAR技術,它代表"寄存器中的SIMD"。
uint32_t average(uint32_t a, uint32_t b) {return (a & b) + (a ^ b) / 2; }方法四
作者還提出了第二種思路,如果無符號32位整形的本身寄存器大小為64位的時候,或者編譯器支持的多字節運算,就可以使用強制轉換的方式:
uint32_t average(uint32_t a, uint32_t b) {// Suppose "unsigned" is a 32-bit type and// "unsigned long long" is a 64-bit type.return ((unsigned long long)a + b) / 2; }不過要注意一點,使用這種方式要保證64位寄存器的前32位為0才可以,要不然計算出來的還是不對的。
方法五
該方法是該長文評論區的一位網友提出的:
uint32_t avg(uint32_t a, uint32_t b) {return (a & b) + ((a ^ b) / 2); }這篇博客長文真就是微軟及各路大神的討論區,學無止境!
ends…
總結
以上是生活随笔為你收集整理的解决求平均值出现加和导致的溢出问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1024,第 15 届「中国内核开发者大
- 下一篇: UART/I2C/SPI/1-wire四