你真的了解模運算嗎

2021-09-30 13:43:55 字數 2217 閱讀 6675

假設我們需要編寫乙個字母表右移對映的程式(可能用於實現某種加密演算法),說起來似乎有些抽象,舉個例子便清晰了:

譬如字母表為 , 右移3位的話, 字母表便被對映為

使用lua,我們簡單編寫了以下**

local code_table = 

local

function

get_map_code(code_index, shift_count)

local code_table_count = #code_table

local map_index = (code_index + shift_count - 1) % code_table_count + 1

return code_table[map_index]

endfor i = 1, #code_table do

print(get_map_code(i, 3))

end

現在我們需要擴充套件程式以支援字母表左移對映的需求,考慮到左移僅是右移的逆操作,我們只要改變shift_count的符號即可~

for i = 1, #code_table do

print(get_map_code(i, -3))

end

執行測試目測沒有問題,nice~

現在我們為了某些考慮(譬如效能),需要將**移植至c/c++,移植完成後的**如下:

const

char codetable = ;

const

int codetablecount = sizeof(codetable) / sizeof(char);

auto getmapcode =

[&codetable, codetablecount](int codeindex, int shiftcount)

;for (auto i = 0; i < codetablecount; ++i)

程式的執行結果與lua是一致的,但是當我們簡單的移植左移的時候,程式的結果卻出錯了……

for (auto i = 0; i < codetablecount; ++i)

問題其實就出在模運算(%)上:

左移操作由於使用了負數的偏移,導致了負數取模運算,而對於負數取模,lua和c/c++的結果是不一致的,進而導致了結果的偏差……

那麼到底lua和c/c++中的負數取模有什麼不一樣呢?我們先從模運算的定義說起~

r = a - i(a / b) * b

其中a為除數,b為被除數,r即為模運算的結果,即餘數,而i(…)代表的是取整函式,取整函式不同,取模結果自然也就不同

對於lua,i(…)使用的是向下取整(floor)的方式,所以如果我們在lua中計算-1 % 5 的話, 有:

r = -1 - floor(-1 / 5) * 5 = -1 - (-1) * 5 = 4

而對於c/c++而言,i(…)使用的是截斷(truncate)的方式,所以如果我們在c/c++中計算-1 % 5 的話, 有:

r = -1 - truncate(-1 / 5) * 5 = -1 - (0) * 5 = -1

由於模運算的結果為負導致索引出界,自然程式的結果也就不會正常了~

知道了程式出錯的原因,「修復」起來也就有了對策,方法很簡單,自己實現乙個使用floor的取模運算即可~

const

char codetable = ;

const

int codetablecount = sizeof(codetable) / sizeof(char);

auto module =

(int dividend, int divisor) -> int

;auto getmapcode =

[&codetable, codetablecount, &module](int codeindex, int shiftcount)

;for (auto i = 0; i < codetablecount; ++i)

值得一提的是如果你使用lua中math.fmod來計算 -1 % 5 的話,結果和c/c++中是一致的,為 -1

模運算看似簡單,但其實大家不一定真正了解,這裡有一段python中關於模運算怎麼實現(同lua一樣,也使用了floor取整)的討論,有興趣的朋友可以看下~

ok,下次再見吧~

你真的了解Java嗎?

三目運算子規則 如果第二個和第三個運算元具有相同的型別,那麼它就是條件表示式的類 型。換句話說,你可以通過繞過混合型別的計算來避免 煩。如果乙個運算元的型別是 t,t 表示 byte short 或 char,而另乙個運算元是乙個 int 型別的常量表示式,它的值是可以用型別 t 表示的,那麼條件表...

你真的了解restful api嗎?

在以前,乙個 的完成總是 all in one 頁面,資料,渲染全部在服務端完成,這樣做的最大的弊端是後期維護,擴充套件極其痛苦,開發人員必須同時具備前後端知識。於是慢慢的後來興起了前後端分離的思想 後端負責資料編造,而前端則負責資料渲染,前端靜態頁面呼叫指定api獲取到有固定格式的資料,再將資料展...

你真的了解git嗎?

看了幾遍廖雪峰的git教程和阮一峰的git教程之後,覺得自己使用git已經是得心應手了,腦中也構建出了一副關於git操作的影象。學習乙個新東西的時候我總是喜歡把知識形象化出乙個圖譜在腦中,這樣記憶的更加深刻。但是隨著使用的深入,我發現我腦中的影象與git的實際行為存在出入。稍微進入一下正題。假如你的...