- 騰訊游戲開(kāi)發(fā)精粹
- 騰訊游戲編著
- 858字
- 2019-08-30 16:20:45
2.3 定點(diǎn)數(shù)的四則運(yùn)算
考慮兩個(gè)定點(diǎn)數(shù)a,b的四則運(yùn)算,它們對(duì)應(yīng)的整數(shù)分別是f(a),f(b),暫不考慮溢出等問(wèn)題。

這意味著兩個(gè)定點(diǎn)數(shù)的和/差,就是這兩個(gè)定點(diǎn)數(shù)對(duì)應(yīng)的整數(shù)的和/差表示的定點(diǎn)數(shù)。

其中,f(a)f(b)是兩個(gè)定點(diǎn)數(shù)對(duì)應(yīng)的整數(shù)的乘積,是這個(gè)乘積再除以2n得到的整數(shù)所表示的定點(diǎn)數(shù)。

其中,是兩個(gè)定點(diǎn)數(shù)對(duì)應(yīng)的整數(shù)的商,
是這個(gè)商再乘以2n得到的整數(shù)所表示的定點(diǎn)數(shù)。而乘以、除以2n的運(yùn)算可以方便地通過(guò)移位操作來(lái)實(shí)現(xiàn)。
2.3.1 加法與減法
浮點(diǎn)數(shù)的加減需要先對(duì)齊階碼(相當(dāng)對(duì)齊小數(shù)點(diǎn)),然后再相加減。而定點(diǎn)數(shù)的小數(shù)點(diǎn)是對(duì)齊的,按照我們之前講的原理,可以直接使用對(duì)應(yīng)的整數(shù)的加減法。
2.3.2 乘法
浮點(diǎn)數(shù)的乘法是通過(guò)整數(shù)部分相乘、階碼相加實(shí)現(xiàn)的。
對(duì)于32位定點(diǎn)數(shù),按照前面所講的原理可以實(shí)現(xiàn)為
std::int32_t a; std::int32_t b; std::int32_t value = (std::int64_t(a) * b) >> 10;
這里做右移44.20(64)→32.10(32)時(shí)存在數(shù)據(jù)信息丟失和溢出的可能。在使用時(shí)要注意這一點(diǎn)。
對(duì)于64位定點(diǎn)數(shù),直接將兩個(gè)定點(diǎn)數(shù)的內(nèi)部64位整數(shù)相乘,需要一個(gè)128位的整數(shù)才能完整表示結(jié)果。
對(duì)于GCC,可以使用_int128_t 128位類(lèi)型,能直接使用128位乘法。
_int64_t value = (_int128_t(a) * b) >> 32;
而對(duì)于VC,目前還沒(méi)有128位整數(shù)類(lèi)型,可以使用intrinsic function _mull128來(lái)計(jì)算,并用兩個(gè)std::int64_t來(lái)存儲(chǔ)結(jié)果。
std::int64_t retHigh = 0; std::int64_t retLow = _mul128(a, b, &retHigh);
在函數(shù)的第三個(gè)參數(shù)中傳入高64位變量的地址,即可以得出計(jì)算結(jié)果的高64位。
可以自定義簡(jiǎn)單的128位數(shù)據(jù)來(lái)存儲(chǔ)內(nèi)部64位整數(shù)相乘的完整結(jié)果。例如:
struct Multiply128 { std::uint64_t Low; std::int64_t High; }
2.3.3 除法
按照前面所講的原理,做除法運(yùn)算需要乘以232,為避免溢出需要引入128位的運(yùn)算。對(duì)于GCC可以直接使用128位整數(shù)除法:
_int64_t value = (_int128_t(a) << 32) / b;
但對(duì)于VC就不行了,在intrinsic function中沒(méi)有128位整數(shù)除法。但是可以用匯編代碼來(lái)實(shí)現(xiàn)。
在C++中聲明:
extern "C" std::int64_t _Div128_64( std::int64_t a_low, std::int64_t a_high, std::int64_t b, std::int64_t* ret );
這里把a(bǔ)_low的值傳給寄存器rcx,把a(bǔ)_high的值傳給rdx,把b的值傳給r8,把ret的指針傳給r9。
用匯編代碼實(shí)現(xiàn):
.code Div128_64 proc mov rax, rcx ; 將a_low的值由rcx傳給rax idiv r8 ; rdx - rax(128位被除數(shù)) / r8(除數(shù)) = rdx(余數(shù)), rax(商) mov[r9], rdx ; 通過(guò)指針傳出余數(shù) ret ; 返回商 ret; Div128_64 endp END
需要注意的是,除出來(lái)的商不能超過(guò)64位,否則CPU會(huì)報(bào)出異常。
在審稿階段,筆者發(fā)現(xiàn)最新版本的Visual Studio 2019已經(jīng)提供了128位整數(shù)除法的intrinsic function _div128(),與匯編版本的功能一致。
- 精通Unreal游戲引擎
- 微信小游戲開(kāi)發(fā):后端篇
- 游戲劇本怎么寫(xiě)
- Android游戲開(kāi)發(fā)實(shí)踐指南
- 網(wǎng)絡(luò)游戲角色設(shè)計(jì)與制作實(shí)戰(zhàn)(第二版)
- OpenGL ES 3.x游戲開(kāi)發(fā)(上卷)
- 游戲編程模式
- J2ME手機(jī)游戲開(kāi)發(fā)詳解
- Unity 5.X 3D游戲開(kāi)發(fā)技術(shù)詳解與典型案例
- 騰訊游戲開(kāi)發(fā)精粹
- 3ds max+Photoshop游戲場(chǎng)景設(shè)計(jì)(第4版)
- 妙趣橫生的游戲制作之旅
- 電子游戲與多元智能培養(yǎng)
- 血戰(zhàn)到底:成都麻將實(shí)戰(zhàn)妙訣
- 游戲藝術(shù):從傳統(tǒng)到現(xiàn)代的發(fā)展歷程