2018q1 Homework2 (assessment) === contributed by < `poorpoorQQ` > ## 1. 前三週作業回顧 這次作業要選出三題隨堂小考題目做分析,選擇條件以看不懂的程式碼以及有較高應用機會為主。故分別選出"浮點數平方根"、"邏輯與運算右移"與"捨入"三題進行探討。期待能對於計算機中浮點數運算與捨入有初步了解。 1. week3 第一題 假設 double 為 64-bit 寬度,考慮以下符合 IEEE 754 規範的平方根程式,請補上對應數值,使其得以運作: ```Clike #include <stdint.h> /* A union allowing us to convert between a double and two 32-bit integers. * Little-endian representation */ typedef union { double value; struct { uint32_t lsw; uint32_t msw; } parts; } ieee_double_shape_type; ``` 之前不知道union是什麼,根據螞蟻書的說明,這段程式碼定義union資料型態,使一段記憶體空間能夠當作 double 或 兩個 uint32 使用。為了方便將64位元的雙精度浮點資料分成兩段處理。(若有認知錯誤,請指正) ```Clike /* Set a double from two 32 bit ints. */ #define INSERT_WORDS(d, ix0, ix1) \ do { \ ieee_double_shape_type iw_u = { \ .parts.msw = ix0, \ .parts.lsw = ix1, \ }; \ (d) = iw_u.value; \ } while (0) /* Get two 32 bit ints from a double. */ #define EXTRACT_WORDS(ix0, ix1, d) \ do { \ ieee_double_shape_type ew_u; \ ew_u.value = (d); \ (ix0) = ew_u.parts.msw; \ (ix1) = ew_u.parts.lsw; \ } while (0) ``` 這裡設定了兩段巨集,展開後分別作為 double 與 兩個 uint_32 互轉用。令人感興趣的是 do while(0) 迴圈,完全不知道要幹嘛。於是關鍵字搜尋 "do while(0)" 找到了以下參考資料 [What is the purpose of using do {...} while (0) in macros?](https://www.quora.com/What-is-the-purpose-of-using-do-while-0-in-macros) 根據參考資料,這種寫法可以避免if判斷式內只有一行程式而沒有用括號包起來的情況下展開巨集,如下 ```Clike #define foo(x) bar(x); baz(x) if (!feral) foo(wolf); ``` 會展開成以下形式 ```Clike if (!feral) bar(wolf); baz(wolf); ``` 也可以避免脫鉤的else,若定義巨集如下 ```Clike #define foo(x) {bar(x); baz(x);} if (!feral) foo(wolf); else bin(wolf); ``` 則if else敘述展開如下, else 跟 if 脫鉤 ```Clike if (!feral) { bar(wolf); baz(wolf); }; else bin(wolf); ``` 接下來進入 sqrt function 先處理特殊情況:零、負數、INF、NaN ```Clike /* take care of INF and NaN */ if ((ix0 & KK1) == KK2) { /* sqrt(NaN) = NaN, sqrt(+INF) = +INF, sqrt(-INF) = sNaN */ return x * x + x; } ``` 上述區段處理 INF 與 NaN ,兩者共通點為 EXPONENT(11bits) 區全為1。因此 KK1 與 KK2 皆為0x7ff00000。註解內提示所有可能情況,針對 NaN 與 INF 的運算定義在IEEE745內,但IEEE不給下載,<font color="red">為避免舉燭,這裡先不探討</font>。 ```Clike /* take care of zero */ if (ix0 <= 0) { if (((ix0 & (~sign)) | ix1) == 0) return x; /* sqrt(+-0) = +-0 */ if (ix0 < 0) return (x - x) / (x - x); /* sqrt(-ve) = sNaN */ } ``` 上述區段處理 x 小於等於零的情況,因為 ix0 宣告為有號數,所以只要其最高位(sign)為 1 ,即可確認其為負,即是說 x 為負。x 為正零的情況則為: ix0 = 0x8fffffff 與 ix1 = 0x00000000 而程式中的 bit mask 則是更為簡潔有效的算法。唯有ix0 = 0x8fffffff成立,ix0 & (~sign)才會是0x00000000。之後與 ix1 作 or 運算,只有兩者皆為0x00000000,判斷式才成立。 <font color="red">負數開根號為 sNaN 同樣地要對 IEEE745 深入了解後再討論</font>。 ```Clike /* normalize x */ m = (ix0 >> 20); if (m == 0) { /* subnormal x */ while (ix0 == 0) { m -= 21; ix0 |= (ix1 >> 11); ix1 <<= 21; } for (i = 0; (ix0 & 0x00100000) == 0; i++) ix0 <<= 1; m -= i - 1; ix0 |= (ix1 >> (32 - i)); ix1 <<= i; } ``` 上述區段處理 denormalized(非正規) 的情況。將非正規數中 MANTISSA 區段的最高有效位左移至最高位,並記錄相對應的指數變化。這裡為了處理位移時有考慮到 MANTISSA 區段包含 ix0 後20位元與 ix1 整體,若為32位元版本可以更加精簡。其中值得注意的是,若 ix0 全為0,程式碼將 ix1 前21位元搬入 ix0 中。<font color="red">為何是搬21位元而不是20位元?</font>當然因為正規化數與非正規化數之間引入了不同的小數位計算方式(有無加1),直接移21位元不會有錯誤,但是這樣設計的理由尚待釐清。 ```Clike m -= KK3; /* unbias exponent */ ix0 = (ix0 & 0x000fffff) | 0x00100000; if (m & 1) { /* odd m, double x to make it even */ ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; } m >>= 1; /* m = [m/2] */ ``` 上述區段處理指數部分。<font color="red">第二行的用意目前還沒想到</font>,就結果看來是把 ix0 第12位元強制轉為1。 if 判斷式內程式碼是用來處理當指數為奇數時的情況,將,方便開平方根時可以除二。奇怪的地方在於為何不寫成 ```Clike if (m & 1) { /* odd m, double x to make it even */ ix0 = (ix0 << 1) + (ix1 >> 31); ix1 <<= 1; } ``` 接下來終於要處理平方根運算了 ```Clike /* generate sqrt(x) bit by bit */ ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; ``` 上述區段的程式碼將 ix0 與 ix1 乘二,但是<font color="red">用意究竟為何</font>,實在想不透。 ```Clike /* generate sqrt(x) bit by bit */ while (r != 0) { t = s0 + r; if (t <= ix0) { s0 = t + r; ix0 -= t; q += r; } ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; r >>= 1; } ``` 上述區段的程式碼可視為常除法開平方根的二進制實作,此段僅處理 ix0 的部分(21bits),所以 r 設為0x00200000。常除法處理平方根除了常除法運算以外,一個大重點為 $(a + b)^2 = a^2 + 2ab + b^2$ 中2ab的處理[(參考資料)](http://highscope.ch.ntu.edu.tw/wordpress/?p=51628),實現方式為在 if 判斷式外先計算 t = s0 + r 但是在 if 內部再計算 s0 = t + r。因為 t 共加了兩次 r ,藉此實現 2ab 的部分。 ```Clike r = sign; while (r != 0) { t1 = s1 + r; t = s0; if ((t < ix0) || ((t == ix0) && (t1 <= ix1))) { s1 = t1 + r; if (((t1 & sign) == sign) && (s1 & sign) == 0) s0 += 1; ix0 -= t; if (ix1 < t1) ix0 -= 1; ix1 -= t1; q1 += r; } ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; r >>= 1; } ``` 上述區段的程式碼功能上如同上一段。其中不同點在於,處理的是 ix1(32bits) 所以 r 設為 sign。這一段還多了一些判斷式處理,<font color="red">關於這點,還沒深入思考</font>。 --- ## 2. *因為自動飲料機而延畢的那一年* & 前三週課程心得 看完那篇文章後,只能說歡迎來到現實世界,而那也是產品開發人員的日常。所以RD別再靠北PM了改規格與壓日期了,大家都有各自的苦衷。其實要避免那樣的情況有很多方法,最重要的是要確立目標。OP君一開始並沒有設定短期目標,只有說要作一台飲料機,還直接想做商品機。首先,可行性都還沒驗證,就開始作種種考量,例如食品安全,是在浪費資金,因為要用上較好的材料。另外,花費太多時間在成本評估上。成本評估在開發初期很難準確,但卻會隨著專案進行逐漸明朗。建議初期大概抓個成本就好,30%誤差都是正常的,之後在隨著專案進行修正。最後是時間控管,尤其有要動用到加工廠或客製品的東西顯得更加重要,OP君也在加工時間上吃足了苦頭。因為OP君並沒有用管線化的開發流程,而是做完一樣之後再著手下一樣,導致等待加工的時間變成閒置。這某種程度也是因為沒有立短期目標的後果。建議的流程是,一開始要把系統模組化,每一模組的目標與要動用的資金、技術、加工時間都要有初步規劃,即便計畫初期就花兩個月規劃都是值得的。根據手上的總資源作分配,要嘛時間換資金,要嘛資金換時間,要拜託別人的,尤其是加工,盡早拜碼頭。要發包的東西快發包,用管線化的方式塞滿自己跟協力廠的時間。省下越多的時間,可以讓計畫更安穩。 經過前三週課程,深深體會到資訊產業與機械電機產業在根本上的不同。資訊產業變化太快了,今天學的東西可能很快就過時了。資訊技術很多東西建立在人們訂定的標準與介面架構上,因此新標準的推出與修訂可以很快。相對而言,機械電機產業的東西大多建立在實驗數據與物理特性上,也許有更好的性能,但基本原理永遠那一套。學習資訊技術有點像學習外國語言文化,需要大量閱讀標準。例如沒有閱讀C語言標準,很多程式運作方式是不確定的。機械電機的東西因為立基於物理特性上,很多東西就算不翻閱標準,也不會影響你對系統的理解。當然這是建立在物理讀通的情況下。資訊技術,對於像我這樣的門外漢而言,學起來確實有點辛苦,但希望能持續下去。希望有一天能夠對機械系統上層的資訊系統有87%的掌握。說起這門課目前對我最大的衝擊就標準的閱讀,所有的寶藏都在標準裡,但讀不讀的完又是另一個故事了。 --- ## 3. 現代處理器設計預習心得 ---
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up