是否有可能通過使用純位移,加法,減法和也許乘以10來除無符號整數?使用資源非常有限且分割緩慢的處理器。使用位移除10?
使用位移除10?
回答
這裏的編制小整型常量部門當微軟編譯器做什麼。假設一個32位機(代碼可相應調整):
int32_t div10(int32_t dividend)
{
int64_t invDivisor = 0x1999999A;
return (int32_t) ((invDivisor * dividend) >> 32);
}
這是怎麼回事這裏要說的是,我們要按照1/10 * 2^32基本接近倍增,然後除去2^32。這種方法可以適應不同的除數和不同的位寬。
這對於ia32體系結構非常適用,因爲它的IMUL指令會將64位產品放入edx:eax中,並且edx值將成爲所需的值。即一個緩慢的乘法指令(假設股息是EAX和商通過在EAX返回)
div10 proc
mov edx,1999999Ah ; load 1/10 * 2^32
imul eax ; edx:eax = dividend/10 * 2 ^32
mov eax,edx ; eax = dividend/10
ret
endp
即使一臺機器上,這將是比軟件更快地劃分。
+1,我想強調一下,編譯器會自動爲你寫這個「x/10」 – Theran 2011-04-05 21:25:35
.hmm,這裏沒有一些數值不準確嗎? – 2011-04-06 12:49:47
做整數除法時,你總是會有數字不準確:當你用整數除以28時,你會得到什麼?答:2。 – 2011-04-06 12:52:57
除法是減法,所以是的。向右移1(除以2)。現在從結果中減去5,計算你減法的次數,直到值小於5.結果是你做的減法的次數。哦,劃分可能會更快。
如果分頻器中的邏輯尚未爲您完成這項工作,那麼採用正常分頻將右移再除以5的混合策略可能會使您獲得性能提升。
當然,你可以,如果你能承受一些精度上的損失。如果你知道你的輸入值的值範圍,你可以提出一個位移和一個精確的乘法。 一些例子,你可以如何劃分10,60,...像這樣的博客描述格式time the fastest way可能。
temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10
你的, 阿洛伊斯·克勞斯
您必須意識到中間值'(ms * 205)'可能溢出。 – 2011-04-05 21:16:04
如果你做int ms = 205 *(i >> 11);如果數字很小,您將得到錯誤的值。您需要一個測試套件來確保在給定的值範圍內結果是正確的。 – 2011-04-05 21:28:29
這對於ms = 0..1028 – 2014-10-18 01:14:49
儘管到目前爲止給出的答案與實際問題相符,但它們與標題不匹配。所以這裏的解決方案深受Hacker's Delight的啓發,它確實只使用了位移。
unsigned divu10(unsigned n) {
unsigned q, r;
q = (n >> 1) + (n >> 2);
q = q + (q >> 4);
q = q + (q >> 8);
q = q + (q >> 16);
q = q >> 3;
r = n - (((q << 2) + q) << 1);
return q + (r > 9);
}
我認爲這是缺乏乘法指令的架構的最佳解決方案。
在一次只能移動一個地方的體系結構上,一系列明顯的比較兩個乘以10的權力可能比黑客的解決方案更好。假設一個16位的分紅:
uint16_t div10(uint16_t dividend) {
uint16_t quotient = 0;
#define div10_step(n) \
do { if (dividend >= (n*10)) { quotient += n; dividend -= n*10; } } while (0)
div10_step(0x1000);
div10_step(0x0800);
div10_step(0x0400);
div10_step(0x0200);
div10_step(0x0100);
div10_step(0x0080);
div10_step(0x0040);
div10_step(0x0020);
div10_step(0x0010);
div10_step(0x0008);
div10_step(0x0004);
div10_step(0x0002);
div10_step(0x0001);
#undef div10_step
if (dividend >= 5) ++quotient; // round the result (optional)
return quotient;
}
您的代碼執行16乘法10.爲什麼您認爲您的代碼比黑客的快樂更快? – chmike 2017-10-07 20:11:46
我的想法並不重要。重要的是在適用的平臺上它是否更快。試試吧!這裏根本沒有普遍最快的解決方案。每個解決方案都有一個平臺,並且在該平臺上工作得最好,可能比任何其他解決方案都要好。 – 2017-10-18 16:09:45
我沒有注意到n * 10是恆定的。它將由編譯器預先計算。我在答案中提供了一種替代算法。除了一個區別之外,我們的算法是等價的您從v中減去b * 10並將其添加到x * 10中。您的算法不需要跟蹤保存變量的x * 10。您顯示的代碼展開我的while循環。 – chmike 2017-10-18 19:10:26
考慮到庫巴奧伯的迴應,還有一個在同一脈絡。它使用迭代逼近的結果,但我不希望有任何令人驚訝的表現。
假設我們必須找到x
其中x = v/10
。
我們將使用逆操作v = x * 10
,因爲它具有很好的屬性,當x = a + b
,然後x * 10 = a * 10 + b * 10
。
讓我們使用x
作爲迄今爲止保持最佳結果近似值的變量。搜索結束後,x
將保留結果。我們將x
的每個位b
從最高有效位設置爲較低有效位,逐個比較(x + b) * 10
與v
。如果其小於或等於v
,則位b
設置在x
中。爲了測試下一個位,我們只需將b一個位置向右移(除以二)。
通過在其他變量中保存x * 10
和b * 10
,我們可以避免乘以10。
我們得到以下算法通過10
uin16_t x = 0, x10 = 0, b = 0x1000, b10 = 0xA000;
while (b != 0) {
uint16_t t = x10 + b10;
if (t <= v) {
x10 = t;
x |= b;
}
b10 >>= 1;
b >>= 1;
}
// x = v/10
編輯來劃分v
:獲得庫巴奧伯的算法,避免了變量x10
的需要,我們可以減去v
b10
和v10
。在這種情況下x10
不再需要。該算法變得
uin16_t x = 0, b = 0x1000, b10 = 0xA000;
while (b != 0) {
if (b10 <= v) {
v -= b10;
x |= b;
}
b10 >>= 1;
b >>= 1;
}
// x = v/10
環路可以unwinded和b
和b10
的不同值可以預先計算爲常數。
- 1. 使用移位除法的問題
- 2. 浮點數mul(* 10)添加和移位
- 3. 使用SSE將8位整數除以4(或移位)
- 4. 乘法和移位除法
- 5. Unity不允許使用位置 - Windows 10
- 6. MIPS高效使用移位
- 7. 使用__builtin_ia32_shufps將矢量移位32位?
- 8. 位運算使用移位寄存器
- 9. 使用左移位和按位或
- 10. 使用PowerShell,Windows 10刪除整個git
- 11. 使用移位運算符除以任意數字
- 12. 爲什麼JDK使用移位而不是乘/除?
- 13. 10位的Android
- 14. 在Win 10 64位上使用Oracle客戶端32位
- 15. 位移用Java
- 16. SQL - 列前10位
- 17. JAVA ISBN-10編號:查找第10位
- 18. 如何刪除移位/減少警告?
- 19. 如何刪除matplotlib相對位移軸
- 20. 如何在Windows 10移動應用程序中使用LumiaImagingSDK.UWP 3.0?
- 21. 如何使用32位除法指令執行64位除法?
- 22. 位移位
- 23. 移位N位
- 24. 位列移位
- 25. 如何使用JavaScript右移10個像素,每秒下移10個像素的方塊
- 26. java中雙除10
- 27. MySQL - Auto_increment除以10
- 28. 使用增量移動css位置
- 29. 使用移位寄存器的java
- 30. 使用位移運算符的錯誤
有可能(重複減法是除法),但問題是它是否比慢速除法更快。 – 2011-04-05 21:07:29
@esnyder。對不起,我無法理解你。你在基地17還是基地22說話? – 2011-04-05 21:39:39
基地大二。如果用「10」表示16位十進制或10h,則右移除2^n即可解決您的問題。 – tamarintech 2011-04-05 21:42:14