TingL7
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 2018q1 Homework2 (assessment) contributed by < `TingL7` > ## 第 1 週 測驗 `一` 考慮以下程式碼: ```clike #include <stdlib.h> int isMultN(unsigned int n) { int odd_c = 0, even_c = 0; /* variables to count odd and even SET bits */ if (n == 0) // return true if difference is 0. return 1; if (n == 1) // return false if the difference is not 0. return 0; while (n) { if (n & 1) // odd bit is SET, increment odd_C odd_c++; n >>= 1; if (n & 1) // even bit is SET, increment even_c even_c++; n = n >> 1; } /* Recursive call till you get 0/1 */ return(isMultN(abs(odd_c - even_c))); } ``` 其作用為檢查輸入整數是否為 N 的倍數,那麼 N 為多少? --- ### 解題想法&思考 * 程式碼解讀 * 由這兩段程式碼,可以得知這是一個遞迴程式,結束條件是 odd_c 和 even_c 相等或相差 1 ,且相等時,為N的倍數。 ```clike if (n == 0) // return true if difference is 0. return 1; if (n == 1) // return false if the difference is not 0. return 0; ``` `return(isMultN(abs(odd_c - even_c)));` * 而由這段程式碼可以知道 odd_c 及 even_c 分別在計算奇數位及偶數位為 1 的個數 ```clike while (n) { if (n & 1) // odd bit is SET, increment odd_C odd_c++; n >>= 1; if (n & 1) // even bit is SET, increment even_c even_c++; n = n >> 1; } ``` * 綜合上述,並簡單化這個題目,在程式只有進行一次遞迴時,奇數位及偶數位總合相等時,即為 N 的倍數。將選項帶入有此特性的只有 3(11~2~) 。 ### 延伸問題: 為什麼上述程式碼可運作呢? >數字與文字間也要空白喔! >[name=課程助教][color=red] >>好的,已修改 >>[name=TingL7] * 程式碼可以運作,是依據 3 的倍數,在二進位的情況下的特性,即奇數位數字和及偶數位數和相等或相差 3 的倍數。因此,只要證明 3 的倍數有此特性,即可確認上述程式碼是運作良好的。 * 這個特性與十進位中的 11 很像。參考[ 100 以內的質數倍數判別法及其延伸](http://www.shs.edu.tw/works/essay/2015/11/2015111502175190.pdf)中, 11 的倍數判別法,可寫出以下證明: 設有一 (n+1) 位數 N = (a~n~a~n-1~..a~1~a~0~)~2~ ,其中 a~n~$\neq$ 0 令 k 為 $\le n$ 的正整數 \begin{align} a_n &=\sum_{i=0}^na_i\times2^i\\ & = [a_0+a_2(3+1)+a_4(15+1)+...+a_{2k}(2^{2k}-1+1)+...]\\ & +[a_1(3-1)+a_3(9-1)+a_5(33-1)+...+a_{2k+1}(2^{2k+1}+1-1)+...]\\ & = (a_0+a_2+a_4+...+a_{2k}+...)\\ & +[a_2(3)+a_4(15)+...+a_{2k}(2^{2k}+1)+...]\\ & - (a_1+a_3+a_5+...+a_{2k+1})\\ & + [a_1(3)+a_3(9)+a_5(33)+...+a_{2k+1}(2^{2k+1}+1)+...]...第一式 \end{align} 利用歸納證明法,可證明 2^2n^-1 及 2^2n+1^+1 為 3 的倍數: n=1:則 2^2^-1=3 為 3 的倍數 令 n=k 成立,即 2^2k^-1=3x , x 可為任一正整數 n=k+1: 2^2(k+1)^-1=2^2k+2^-1=4\*2^2k^-1=4(2^2k^-1)+3=4(3x)+3=3(4x+1) 得證: 2^2n^-1 為 3 的倍數 因此, [a~2~(3)+a~4~(15)+...+a~2k~(2^2k^+1)+...] 為 3 的倍數...**第二式** n=0:則 2^1^-1=3 為 3 的倍數 令 n=k 成立,即 2^2k+1^+1=3x , x 可為任一正整數 n=k+1: 2^2(k+1)+1^+1=2^2k+3^+1=4\*2^2k+1^+1=4(2^2k+1^+1)-3=4(3x)-3=3(4x-1) 得證: 2^2n+1^-1 為 3 的倍數 因此, [a~1~(3)+a~3~(9)+a~5~(33)+...+a~2k+1~(2^2k+1^+1)+...] 為 3 的倍數...**第三式** 由第一、二、三式可得知: 如果 (a~0~+a~2~+a~4~+...+a~2k~+...)-(a~1~+a~3~+a~5~+...+a~2k+1~+...) 為 3 的倍數,則 N 必為 3 的倍數。 因此,要判定二進位中,是否為 3 的倍數,可由 奇數位數字和及偶數位數和相等或相差 3 的倍數 的特性去判斷。 ### 延伸問題: 將 N 改為其他質數再改寫為對應的程式碼,一樣使用遞迴 * 要將 N 改為其他質數,要先找出其他質數是否類似 3 的特殊判斷特性。 * N=5 * 參考[從二進位判斷數字是否被 5 整除](https://www.ptt.cc/bbs/Prob_Solve/M.1223098109.A.99F.html), N 是否為 5 的倍數判定,可由 (N>>2)-(N&3) 是否為 5 的倍數決定。 * 證明: (N>>2)-(N&3) 為 N 除以 4 的商數 (a) 減去 N 除以 4 的餘數 (b)。寫成算式為 $N\div4=a...b\\ =>N =a\times4+b=a\times5-a+b\\ =>如果 b-a 為 5 的倍數,則 N 為 5 的倍數$ * 程式碼 ```clike #include <stdlib.h> int isMultN(unsigned int n) { if (n == 0|| n == 5) // return true if n is 0 or 5. return 1; if (n < 5) // return false n is smaller than 5 return 0; n = (n >> 2) + (n & 3); /* Recursive call till n <= 5 */ return(isMultN(n)); } ``` * N=7 * 由 N=5 的證明,是推廣到 N=7 ,甚至可以說是任何數字都可以用,只要找出與 2 的次方的關係。 * $N =a\times8+b=a\times7+(a+b)\\ =>如果 a+b 為 7 的倍數,則 N 為 7 的倍數$ * 程式碼 ```clike #include <stdlib.h> int isMultN(unsigned int n) { if (n == 0|| n == 7) // return true if n is 0 or 7. return 1; if (n < 7) // return false n is smaller than 7. return 0; n = (n >> 3) + (n & 7); /* Recursive call till n <= 5 */ return(isMultN(n)); } ``` * N=3 * 由上述推得的方法,可以將 N=3 改寫。 * 改寫一: $N =a\times4+b=a\times3+(a+b)\\ =>如果 a+b 為 3 的倍數,則 N 為 3 的倍數$ * 程式碼 ```c #include <stdlib.h> int isMultN(unsigned int n) { if (n == 0|| n == 3) // return true if n is 0 or 3. return 1; if (n < 3) // return false n is smaller than 3. return 0; n = (n >> 2) + (n & 1); /* Recursive call till n <= 5 */ return(isMultN(n)); } ``` * 改寫二: $N =a\times2+b=a\times3+(-a+b)\\ =>如果 b-a 為 3 的倍數,則 N 為 3 的倍數$ * 程式碼 ```c #include <stdlib.h> int isMultN(unsigned int n) { if (n == 0) // return true if n is 0. return 1; if (n == 1) // return false n is 1. return 0; n = (n >> 1) - (n & 1); /* Recursive call till n <= 5 */ return(isMultN(n)); } ``` * 通用 * 其實,上面的方法是可以通用的,不論是否為質數,只要算清楚與 2 的次方差多少,再利用 shift 及 + 或 - 就可以判斷倍數。 ## 第 2 週 測驗 `一` ![](https://i.imgur.com/Mp0cHII.jpg) 請完成下方程式碼,依循 IEEE 754 單精度規範,輸出 $2^x$ 的浮點數表達法,而且考慮兩種狀況: - 當 $x$ 過小的時候,回傳 $0.0$ - 當 $x$ 過大的時候,回傳 $+∞$ 注意:這裡假設 `u2f` 函式返回的浮點數值與其無號數輸入有著相同的位數,也就是至少 32-bit ```c #include <math.h> static inline float u2f(unsigned int x) { return *(float *) &x; } float exp2_fp(int x) { unsigned int exp /* exponent */, frac /* fraction */; /* too small */ if (x < 2 - pow(2, Y0) - 23) { exp = 0; frac = 0; /* denormalize */ } else if (x < Y1 - pow(2, Y2)) { exp = 0; frac = 1 << (unsigned) (x - (2 - pow(2, Y3) - Y4)); /* normalized */ } else if (x < pow(2, Y5)) { exp = pow(2, Y6) - 1 + x; frac = 0; /* too large */ } else { exp = Y7; frac = 0; } /* pack exp and frac into 32 bits */ return u2f((unsigned) exp << 23 | frac); } ``` --- ### 解題想法&思考 * 要解這題,需要充分了解浮點數的邊界判定。 * 浮點數 = (-1)^s^ \* 2^E^ \* M * bias = 2^8-1^-1 = 127 * denormalized:(0~1) * s: +/-,本題中沒有負的 * E: 1-bias= -126 = 2-2^7^ * M: 0.f~22~f~21~f~20~...f~0~ * normalized:(1~max) * s: +/-,本題中沒有負的 * E: e-bias = e+1-2^7^ * M: 1.f~22~f~21~f~20~...f~0~ * too small ``` if (x < 2 - pow(2, Y0) - 23) { exp = 0; frac = 0; ``` * 判斷d enormalized 可以表達的最小數值,在單精度的情況下為 `0 00000000 00000000000000000000001` 即為 $(-1)^0 \times 2^{2-2^{7}} \times 2^{-23}=2^{2-2^{7}-23}$ 因此, ++Y0=7++ * x 太小,回傳 0.0 。 * denormalized ``` } else if (x < Y1 - pow(2, Y2)) { exp = 0; frac = 1 << (unsigned) (x - (2 - pow(2, Y3) - Y4)); ``` * 判斷 denormalized 可以表達的最大數值為 `0 00000000 11111111111111111111111` 即為 $(-1)^0 \times 2^{2-2^{7}} \times \sum_{i=1}^{23}2^{-i}=2^{2-2^{7}} \times (1-2^{-23})$ * (1-2^-23^) 取近似值為 1 ,因此 ++Y1=2++ , ++Y2=7++ 。 * $2^x = 2^{2-2^7}\times(frac\times2^{-23})$ ,則 $frac=2^{x-(2-2^7-23)}$ 因此, ++Y3=7++ , ++Y4=23++ 。 * normalized ``` } else if (x < pow(2, Y5)) { exp = pow(2, Y6) - 1 + x; frac = 0; ``` * 判斷 normalized 可以表達的最大數值為 `0 11111110 11111111111111111111111` 即為 $(-1)^0 \times 2^{2^8-1-1-(2^7-1)} \times (1+1-2^{-23})=2^{2^7-1}\times (2-2^{-23})=2^{2^7}-2^{104}$ * 最大可表示的值不會超過 $2^x$ 為 $2^{2^7}$ ,因此 ++Y5=7++ 。 * 由於要表達 2 的次方,不需要小數點後的表示,因此 frac=0 。 * $2^x = 2^{exp-bias}=2^{exp-(2^7-1)}$ ,則 $exp=x+(2^7-1)$ 因此, ++Y6=7++ 。 * too large ``` } else { exp = Y7; frac = 0; } ``` * x 太大,回傳 $+∞$, INF 表示 exp 為 `11111111` = `0xFF` , frac=0 。 因此, ++Y7=0xFF++ 。 ### 延伸問題: 給定一個單精度的浮點數值,輸出較大且最接近的 $2^x$ 值,需要充分考慮到邊界 * 邊界 * denormalized 最小值: `0 00000000 00000000000000000000001` = $(-1)^0 \times 2^{2-2^{7}} \times 2^{-23}=2^{2-2^{7}-23}$ * denormalized 最大值: `0 00000000 11111111111111111111111` = $(-1)^0 \times 2^{2-2^{7}} \times \sum_{i=1}^{23}2^{-i}=2^{2-2^{7}} \times (1-2^{-23}) = 2^{log_2(2^{-126}-2^{-149})}$ * normalized 最大值: `0 11111110 11111111111111111111111` = $(-1)^0 \times 2^{2^8-1-1-(2^7-1)} \times (1+1-2^{-23})=2^{2^7-1}\times (2-2^{-23})=2^{log_2(2^{128}-2^{104})}$ * exp&frac * too small * 為 0 * exp = 0 * frac = 0 * denormalized * exp = 0 * $2^x = 2^{2-2^7}\times(frac\times2^{-23})$,則$frac=2^{x-(2-2^7-23)}$ * normalized * $2^x = 2^{exp-bias}\times(1+frac\times 2^{-23})=2^{exp-127}\times (1+frac\times 2^{-23})$ * $exp=127+x(整數部分)$ * $frac=(2^{x(小數部分)}-1)\times 2^{23}$ * too large * INF * exp = 0xFF * frac = 0 * 程式碼 ```c #include <stdio.h> #include <stdlib.h> #include <math.h> static inline float u2f(unsigned int x) { return *(float *) &x; } float exp2_fp(float x) { unsigned int exp /* exponent */, frac /* fraction */; float frac_f; /* too small */ if (x < 2 - pow(2, 7) - 23) { exp = 0; frac = 0; /* denormalize */ } else if (x < log(pow(2, -126) - pow(2, -149))/log(2)) { exp = 0; frac_f = pow(2.0, (x + 149)); frac = ((int)(frac_f*2)&1)?(int)frac_f+1:(int)frac_f;//向上捨入 /* normalized */ } else if (x < log(pow(2, 128) - pow(2, -104))/log(2)) { exp = 127 + (int)((x<0)?x-0.99:x);//x為負數時,無條件進位 frac_f = ((pow(2.0,x-(float)((int)((x<0)?x-0.99:x)))-1.0)*(1<<23)); frac = ((int)(frac_f*2)&1)?(int)frac_f+1:(int)frac_f;//向上捨入 /* too large */ } else { exp = 0xFF; frac = 0; } /* pack exp and frac into 32 bits */ return u2f((unsigned) exp << 23 | frac); } ``` * 執行結果 ``` 2^-150 exp2_fp():0.000000000000000000000000000000000000000000000000000000000000 powf():0.000000000000000000000000000000000000000000000000000000000000 2^-149.5 exp2_fp():0.000000000000000000000000000000000000000000000000000000000000 powf():0.000000000000000000000000000000000000000000001401298464324817 2^-127 exp2_fp():0.000000000000000000000000000000000000005877471754111437500000 powf():0.000000000000000000000000000000000000005877471754111437500000 2^-126.3 exp2_fp():0.000000000000000000000000000000000000009547961485342182800000 powf():0.000000000000000000000000000000000000009547961485342182800000 2^-126 exp2_fp():0.000000000000000000000000000000000000011754943508222875000000 powf():0.000000000000000000000000000000000000011754943508222875000000 2^127 exp2_fp():170141183460469230000000000000000000000.000000000000000000000000000000000000000000000000000000000000 powf():170141183460469230000000000000000000000.000000000000000000000000000000000000000000000000000000000000 2^127.5 exp2_fp():240615965050037600000000000000000000000.000000000000000000000000000000000000000000000000000000000000 powf():240615965050037600000000000000000000000.000000000000000000000000000000000000000000000000000000000000 2^128 exp2_fp():1.#INF00000000000000000000000000000000000000000000000000000000 powf():1.#INF00000000000000000000000000000000000000000000000000000000 2^0.5 exp2_fp():1.414213538169860800000000000000000000000000000000000000000000 powf():1.414213538169860800000000000000000000000000000000000000000000 ``` * 結果探討 :::info - 可以從結果看出,基本上與 powf() 的結果相同,除了 -149.5 ,為何 -149~-150 之間仍可以表示? - (2^0.5^)^2^=2,但 1.41421353816986080^2^=1.99999993154 ,比實際值略小,如何輸出較大且最接近的 $2^x$ 值? ::: :::warning 這發現很重要!你應該要能從數學的角度計算出上述程式碼和 powf(x, 0.5) 以及理論值的差異,在 IEEE 754 中,已有明確規範。 另外,請改善程式碼縮排,一率用 4 個 space,而非 tab,務必讓版面簡潔清爽。 :notes: jserv ::: ## 第 3 週 測驗 `一` 延續 [從 2 – √ 2 的運算談浮點數](https://hackmd.io/s/ryOLwrVtz),假設 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; /* 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) static const double one = 1.0, tiny = 1.0e-300; double ieee754_sqrt(double x) { double z; int32_t sign = 0x80000000; uint32_t r, t1, s1, ix1, q1; int32_t ix0, s0, q, m, t, i; EXTRACT_WORDS(ix0, ix1, x); /* take care of INF and NaN */ if ((ix0 & KK1) == KK2) { /* sqrt(NaN) = NaN, sqrt(+INF) = +INF, sqrt(-INF) = sNaN */ return x * x + x; } /* 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 */ } /* 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; } 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] */ /* generate sqrt(x) bit by bit */ ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ r = 0x00200000; /* r = moving bit from right to left */ 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; } 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; } /* use floating add to find out rounding direction */ if ((ix0 | ix1) != 0) { z = one - tiny; /* trigger inexact flag */ if (z >= one) { z = one + tiny; if (q1 == (uint32_t) 0xffffffff) { q1 = 0; q += 1; } else if (z > one) { if (q1 == (uint32_t) KK4) q += 1; q1 += 2; } else q1 += (q1 & 1); } } ix0 = (q >> 1) + 0x3fe00000; ix1 = q1 >> 1; if ((q & 1) == 1) ix1 |= sign; ix0 += (m << KK5); INSERT_WORDS(z, ix0, ix1); return z; } ``` --- ### 解題想法與思考 * 程式碼解讀 ```clike=6 typedef union { double value; struct { uint32_t lsw; uint32_t msw; } parts; } ieee_double_shape_type; ``` * 建立union,為了能夠分別操作前後32bit,如下所示。 |bit|63|62 ~~~ 52|51 ~~~~~~ 32|31 ~~~~~~~~~~ 0| |--|:---:|:---:|:---:|:---:| |value|s|exp|frac|frac| |parts|msw|msw|msw|lsw| | |31|30 ~~~ 20|19 ~~~~~~ 0|31 ~~~~~~~~~~ 0| ```clike=14 /* 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) ``` * 這兩段 define 主要功能是使 union 中的 double 和 parts 互相轉換。 * #define在做的事,基本上就是代換,將程式其他地方出現 INSERT_WORDS(d, ix0, ix1) ,代換成下面一段程式碼,不做任何修改。通常在使用會以 `INSERT_WORDS(d, ix0, ix1);` 的樣子出現,因此在代換的程式碼中,最後不會在加 `;`。 * 因為直接代換的關係,可能會產生 dangling else ,所以需要加上 `do{...}while(0)`。 * dangling else : 在使用 if-then(-else) 時,產生語意不清的情況,如: `if A then if B then C else D` ,會有兩種解讀: `if A then (if B then C)else D` 或 `if A then (if B then C else D)` * [Linux Kernel 巨集 do{...}while(0) 的撰寫](http://www.runpc.com.tw/content/content.aspx?id=108451)中,舉了一個很好的例子,將其簡化來看: ```c #define func(A, B) \ { \ if(A) \ B = 1; \ else \ B = 0; \ } \ int funcA(int A,int B){ if(B) func(A, B); else printf("Error B"); return B; } ``` 將 func(A,B) 用define的內容代換: ```c int funcA(int A,int B){ if(B) { if(A) B = 1; else B = 0; }; else printf("Error B"); return B; } ``` 可以發現一個很尷尬的地方,else 前面有 `;` ,表示if在else之前就做完了, else 是多的,語意與原本要表達的意思完全不同,這就是dangling else的情況。 若加上 `do{...}while(0)` 即可避免這樣的情況。 ```c int funcA(int A,int B){ if(B) do{ if(A) B = 1; else B = 0; }while(0); else printf("Error B"); return B; } ``` ```clike=44 /* 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 的情況,要判斷是否為這兩者,只要看exp是否全為 1 ,對 ix0 來說, bit 20~30 的位置為 exp ,因此如果是 INF 或 NaN , ix0 必為 `x 11111111111 xxxxxxxxxxxxxxxxxxx` ,需要在意的只有 exp 那一段,因此 ++KK1 = `0 11111111111 0000000000000000000` = 0x7FF00000++ * 且, exp 必須全為 1 ,因此, ++KK2 = 0x7FF00000++ 。 * 負數開根號為 sNaN,因此,回傳值不能直接回傳 x 。要用-INF得到sNaN,要先了解INF的運算: * +INF + +INF = +INF * -INF + -INF = -INF * +INF + -INF = +INF - +INF = -INF - -INF = sNaN * +INF * +INF = +INF * -INF * -INF = +INF * +INF * -INF = -INF 基本上,要得到sNaN,就必須使 +INF 和 -INF 相加,而 -INF 得到 +INF 的方法只能乘以自己,因此,回傳值為 `x * x + x` 才符合要求。 * 那麼,如果想要不用乘法做回傳值,且完成sqrt(NaN) = NaN, sqrt(+INF) = +INF, sqrt(-INF) = sNaN,有什麼辦法? * 先判斷負號,讓 -INF 在進到這個if之前,就因為負號被驅逐了,這樣就可以直接回傳 x 。 ```clike=49 /* 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 */ } ``` * 判斷為 0 或負數,+-0 的開根號為 +-0 ,負數的開根號為 sNaN 。可由 ix0 判斷,這也是為什麼 ix0 的 type 要為 int32_t 而非 uint32_t 。 * x 為 0 ,除了 sign bit 無所謂以外,其他 bit 都為 0 。拆成 msw 及 lsw 兩部份來看: `ix0 & (~sign)` 即是判斷 msw 中除了 sign bit 以外,其他 bit 是否全為 0 , `ix1` 就是lsw全部是否為0。 * x 為負數,須回傳sNaN。 ```clike=56 /* 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));//x << i ix1 <<= i; } ``` * `m = (ix0 >> 20);`,表示 m 為 sign + exp 。不過,透過前面的if負數判斷, sign 必定為 0 。 * 在非正規化(exp = 0)的情況下,先將它變成正規化,透過 shift 來操作。先找出 frac 中,第一個 1 的位置j,再將其移到 exp 的最小位,並修改 m 。也就是說,原本 x = 2^1-bias^ * f ,現在 x = 2^e-bias^ * (f * 2^j^) ,因此,實際上 x = 2^m^ * 2^-bias^ * (f * 2^j^) , m = -j+1,1代表e * 假設原本 f 為 0001010...0,視為 0.0001010...0, shift 後, f 為 (1) 010...0 ,視為 1.010...0。不需要再考慮是否加一,這大概是非正規化數 exp 和 frac 設計巧妙的地方。 ```clike=70 m -= KK3; /* unbias exponent */ ix0 = (ix0 & 0x000fffff) | 0x00100000; ``` * 之後,又進一步將 2^-bias^ 也包進 2^m^ 之中,同時,修改 ix0 exp 的部分為 0x001。此時, x = x = 2^m^ * f' (f' = f * 2^j^ , m = e - j - bias) 因此,++KK3 = bias = 2^11-1^-1 = 1023++。 ```clik=72 if (m & 1) { /* odd m, double x to make it even */ ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; } m >>= 1; /* m = [m/2] */ ``` * 這段要做 exp 部份的開根號,基本上就是次方除以二。但要考慮到 m 可能是奇數的情況。 * m 如果是奇數的話,要將 exp+frac 的部分乘以二,以表示會被忽略的部份。 ```clike=78 /* generate sqrt(x) bit by bit */ ix0 += ix0 + ((ix1 & sign) >> 31); ix1 += ix1; q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ r = 0x00200000; /* r = moving bit from right to left */ 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; } 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; } ``` * 參考[二進制根式法](https://home.gamer.com.tw/creationDetail.php?sn=2032528)、[wikipedia](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2))、[√Square Roots](http://www.azillionmonkeys.com/qed/sqroot.html),了解直式開根號的方法,基本上就對應了這段程式碼。兩個while迴圈,基本上就是對ix0及ix1做開根號的動作。 ```clike=115 /* use floating add to find out rounding direction */ if ((ix0 | ix1) != 0) { z = one - tiny; /* trigger inexact flag */ if (z >= one) { z = one + tiny; if (q1 == (uint32_t) 0xffffffff) { q1 = 0; q += 1; } else if (z > one) { if (q1 == (uint32_t) KK4) q += 1; q1 += 2; } else q1 += (q1 & 1); } } ``` * 這一段在做捨入, IEEE 754 預設捨入方法是向偶數捨入,但我看不懂 z 是在做什麼。 * 在需要進位的情況下,因為[q,q1]實際為一組數字,如果 q1 全為 1 的話,進位之後,會變成 1 0000...00。 * q1 + 2 之後,q 需要 +1 ,這就表示 ++KK4 = 1 000...00-2 = 111...10 = 0xfffffffe++ 。 * `q1 += (q1 & 1);` 就比較單純,表示向偶數捨入。 * 直接跑過程式,看能不能幫助理解。結果,都如我所預測,`z = one - tiny` 由於 tiny 很小,會被忽略,所以 z == one ,進入第二層 if ,在 q1沒有剛好為 `0xffffffff` 時,會直接進入第二個 else , 因為 `z = one + tiny;` 的 tiny 也會被忽略,所以 z == one 。 * tiny = 1.0e-300,double可表達的範圍約1.7e-308~1.7e-308 :::info - `z = one - tiny; /* trigger inexact flag */` `if (z >= one)`的目的是什麼?在什麼情況下會出現 z > 0 ? - 在什麼情況下,tiny 不會被忽略? ::: ```clike=131 ix0 = (q >> 1) + 0x3fe00000; ix1 = q1 >> 1; if ((q & 1) == 1) ix1 |= sign; ix0 += (m << KK5); INSERT_WORDS(z, ix0, ix1); return z; ``` * exp 要將 1-bias 加回去,因此加上 0x3fe00000 ,此時為 2^0^。 * m 為開根號後的 exp ,要加回 ix0 ,exp 在20~30的位置,因此,要做 shift ,且 ++KK5 = 20++ 。 * 最後再將 ix0 和 ix1 合成 z 就大功告成了~~~ ### 延伸題目: 改為 float (單精度) 版本,注意應該用更短的程式碼來實作 * 想法與思考 * 要注意 ix0 和 ix1 的運算,float不需要拆成兩部分。 * 不需要拆成兩部份,是否還需要union的轉換? float 無法做 bitwise 的操作,需要轉換成int才行。 * 先做負號判斷,再做 +INF and NaN ,這樣可以直接回傳 x 。 -INF 的部分,在負號判斷時,已回傳 sNaN 。 * bias = 2^8-1^ -1 = 127 * tiny 改為1e-30,因為 float 可表達的範圍約3.4e-38~e.4e-38 * 程式碼 ```clike= #include <stdint.h> /* A union allowing us to convert between a float and a 32-bit integers.*/ typedef union { float value; uint32_t v_int; } ieee_float_shape_type; /* Set a float from a 32 bit int. */ #define INSERT_WORDS(d, ix0) \ do { \ ieee_float_shape_type iw_u; \ iw_u.v_int = (ix0); \ (d) = iw_u.value; \ } while (0) /* Get a 32 bit int from a float. */ #define EXTRACT_WORDS(ix0, d) \ do { \ ieee_float_shape_type ew_u; \ ew_u.value = (d); \ (ix0) = ew_u.v_int; \ } while (0) static const float one = 1.0, tiny = 1.0e-30; float ieee754_sqrt(float x) { float z; int32_t sign = 0x80000000; uint32_t r; int32_t ix0, s0, q, m, t, i; EXTRACT_WORDS(ix0, x); /* take care of zero */ if (ix0 <= 0) { if ((ix0 & (~sign)) == 0) return x; /* sqrt(+-0) = +-0 */ if (ix0 < 0) return (x - x) / (x - x); /* sqrt(-ve) = sNaN */ } /* take care of +INF and NaN */ if ((ix0 & 0x7ff00000) == 0x7ff00000) { /* sqrt(NaN) = NaN, sqrt(+INF) = +INF,*/ return x; } /* normalize x */ m = (ix0 >> 23); if (m == 0) { /* subnormal x */ for (i = 0; (ix0 & 0x00800000) == 0; i++) ix0 <<= 1; m -= i - 1; } m -= 127; /* unbias exponent */ ix0 = (ix0 & 0x007fffff) | 0x00800000; if (m & 1) { /* odd m, double x to make it even */ ix0 += ix0; } m >>= 1; /* m = [m/2] */ /* generate sqrt(x) bit by bit */ ix0 += ix0; q = s0 = 0; /* [q] = sqrt(x) */ r = 0x01000000; /* r = moving bit from right to left */ while (r != 0) { t = s0 + r; if (t <= ix0) { s0 = t + r; ix0 -= t; q += r; } ix0 += ix0; r >>= 1; } /* use floating add to find out rounding direction */ if (ix0 != 0) { z = one - tiny; /* trigger inexact flag */ if (z >= one) { z = one + tiny; if (z > one) q += 2; else q += (q & 1); } } ix0 = (q >> 1) + 0x3f000000; ix0 += (m << 23); INSERT_WORDS(z, ix0); return z; } ``` * 執行結果 ``` sqrt 1e+78 1.#INF00 sqrt -1e+78 -1.#IND00 sqrt 9 3.000000 sqrt 1.44 1.200000 sqrt 17453723534 132112.546875 ``` ## [因為自動飲料機而延畢的那一年](http://opass.logdown.com/posts/1273243-the-story-of-auto-beverage-machine-1)及學習本課程 3 週之後的感想與啟發 - 現實與想像的落差。 現實其實有更多的層面需要考量,我們時常將事情簡單化,無論是面對自以為龐大的專案,還是認為很簡單的幾個流程,實際上都是需要經過很多的研究時間、成本。我沒想過,一隻十塊錢不到的原子筆、坐上去舒服的椅子,這些生活用品是怎麼做出來的。 - 學用落差。 我曾經以為,土木系學用落差嚴重只是因為我們不可能真的找一個地方給房子、蓋橋,只能紙上談兵,那牽涉到太多人力、成本。而資訊系這方面的問題,比較沒那麼嚴重,畢竟寫程式每個人的電腦都可以做,而且馬上就可以看到結果。實際上不是,寫寫小程式不難,就像土木系在做模型時一樣,但面對大一點的就容易出現問題了。 - 全心投入。 看完這片文章,讓我想起一年前,為了做一個小房子的模型,而做過無數的實驗跟研究。那時是要蓋一個混凝土的模型,原本的想像也是很簡單,設計尺寸、做模、灌水泥、拆模。但實際下去做之後,才發現現實世界有太多問題,那時為了做這件事,整天都在想、在試。做完之後,成果也不怎麼樣,最後也是收到角落。而這之中,最寶貴的,我想,不是結果多厲害,而是在那段時間全心投入的自己。在實習過後,體會更深的,工作之餘,想要再將心力投入到其他事情上,是沒有那麼容易的,尤其是需要耗費腦力的事。希望自己能一直保有那份熱情。 - 關於課程 一開始覺得壓力很大,這種上課模式沒有遇過 : 每週都小考,還有很多課後學習的教材要看,作業還會被同學批評、觀摩 ~~(害羞)~~,一週花費超過16個小時在上面,不過,還是很多教材沒看完。但,這好像比較接近我想像中的大學課程,有點半自學的性質,也可以說很幸運的在最後一個學期修到這門課。 - 程式寫得好的人,數學都很厲害。 這三、四週下來,深深的體悟到什麼是真正在用的程式。其實很多事情的原理並不難,只是我沒有好好理解過,隨堂考的題目要解出來其實不難,但是在沒有細細看過程式前,很多都是靠感覺猜的,因為那些背後的理論我尚未融會貫通。但是,要完全看懂程式碼在做什麼,為什麼要那樣做就完全是另外一回事了,這大概就是理論跟實做的差距吧。 ## 「[課後學習](http://wiki.csie.ncku.edu.tw/sysprog/schedule)」教材和 [CS:APP 3/e](http://csapp.cs.cmu.edu/)心得和提問

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    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

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully