[小技巧] 倍數的向上取整和向下取整


啥叫倍數的向上取整與向下取整呢?

舉個例子,你有一個函數,用來分配一塊內存,為了提高運行速度,要對內存大小進行對齊。

function NewMemBlock(const BlockSize, AlignSize: Integer): Pointer;
var
  NewSize: Integer;
begin
  NewSize := ...
  GetMem(Result, NewSize);
end;

假設 BlockSize = 10,AlignSize = 8

所謂向下取整,即 NewSize = 8,是很簡單的:

NewSize := BlockSize div AlignSize * AlignSize;
// 10 div 8 * 8 => 1 * 8 => 8

這是因為 div 只會取結果的整數部分而忽略小數部分。

向上取整則需要判斷余數,余數不為 0 則需要補齊缺少的部分:

// 0.
NewSize := BlockSize;
if BlockSize mod AlignSize <> 0 then
  Inc(NewSize, AlignSize - BlockSize mod AlignSize);
// 10 mod 8 <> 0 => Inc(NewSize, 8 - 10 mod 8) => Inc(10, 6) => 16

這個代碼能達到目的,但是顯得很繁瑣,我們來簡化一下,下面幾種方法都可以實現:

// 1.
NewSize := BlockSize + Integer(BlockSize mod AlignSize <> 0) * AlignSize - (BlockSize mod AlignSize);
// 10 + Integer(True) * 8 - (10 mod 8) => 10 + 8 - 2 => 16

// 2.
NewSize := (BlockSize div AlignSize + Integer(BlockSize mod AlignSize <> 0)) * AlignSize;
// (10 div 8 + Integer(True)) * 8 => (1 + 1) * 8 => 16

// 3.
NewSize := Math.Ceil(BlockSize / AlignSize) * AlignSize;
// Math.Ceil(1.25) * 8 => 2 * 8 => 16

代碼簡化了不少,但是方法 1、2 還是有點長,方法 3 用到了浮點數運算,你懂的。

有沒有更簡單的辦法呢?當然有!一般人我不告訴他~~~請看終級代碼:

NewSize := (BlockSize + AlignSize - 1) div AlignSize * AlignSize;
// (10 + 8 - 1) div 8 * 8 => 17 div 8 * 8 => 2 * 8 => 16

這個方法不僅代碼簡潔,而且執行效率是最高的,比方法 0、1、2 快 1 倍,比方法 3 快 20 倍左右。

至於原理,大家自己去理解一下代碼吧~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM