---
# System prepended metadata

title: 2020q3 Homework5 (quiz5)

---

# 2020q3 Homework5 (quiz5)
contributed by < `fwfly` >

## 測驗1

### 答案
```c=
double divop(double orig, int slots) {
    if (slots == 1 || orig == 0)
        return orig;
    int od = slots & 1;
    double result = divop(orig / 2, od ? (slots + 1) >> 1 : slots >> 1);
    if (od)
        result += divop(result, slots);
    return result;
}
```
### 想法
:::info
想看解法可以直接跳到 [程式說明](https://hackmd.io/QIY8ZRyQQN-SpKnb7sq-FQ?both#%E7%A8%8B%E5%BC%8F%E8%AA%AA%E6%98%8E)
:::

一開始是從答案開始了解題目，
後來找到了這篇，應該是以前同學做的說明而知道程式碼的運作
- https://hackmd.io/@billsun/B1gii716N

但是單純這樣並沒有辦法去解釋為什麼要 "除 2" 這樣的問題
所以又再去翻找了怎麼從整數轉換成浮點數．
從中知道了  IEEE754 浮點數表示法(怎麼用 binary 去表示服點數)
但是在 wiki 上有說
https://en.wikipedia.org/wiki/IEEE_754
:::info
base (also called radix) b, which is either 2 (binary) or 10 (decimal) in IEEE 754
:::

意思是浮點數的 base 可以為 2 跟為 10，
`原則上` c 語言應該是用 base 2 `感覺上` base 2 也比較符合 binary 的操作，不過這樣無法保證所以去翻了 C99 規格書．
在 [C99](http://www.dii.uchile.cl/~daespino/files/Iso_C_1999_definition.pdf) 規格書 (5.2.4.2.2, p25) 跟原始碼中可以得知，兩種 base 都是有實作

FLT_RADIX 2     # base 2 
https://github.com/gcc-mirror/gcc/blob/master/gcc/ginclude/float.h#L33

DECIMAL_DIG 10  # base 10
https://github.com/gcc-mirror/gcc/blob/master/gcc/ginclude/float.h#L209

由於兩種都有實作，因此最簡單的方法是直接印出 binary．至少可以知道目前的 gcc 是跑哪一種 base.
```c=
static void printme(void *c, size_t n)
{
  unsigned char *t = c;
  if (c == NULL)
    return;
  while (n > 0) {
    int q;
    --n;
    for(q = 0x80; q; q >>= 1)
      printf("%x", !!(t[n] & q));
  }
  printf("\n");
}

void fpp(float f)
{
  printf("%f\n", f);
  printme(&f, sizeof f);
}
```
結果
```
3.600000 # input
01000000011001100110011001100110
```
在跟以下網站比對
- [IEEE 754 Converter](https://www.h-schmidt.net/FloatConverter/IEEE754.html) (這個網站是以 base 2 去跑浮點數)

可以知道在我的筆電上的 gcc 是 base 2 的浮點數．

**base 2 浮點數的 2 的除法跟乘法**
如果是 base 2 的浮點數，做 2/2的次方 的乘除法就會變得很簡單，只需要對 Exponent 的部分做加減法的運算就好．
不過這一部分依然無法確定是否 "除2" 是否會對浮點數運算有幫助


以下解釋都是以 base 2 浮點數為基礎


### 程式說明
這題是透過**分子分母共同除以 2** 去逼近答案．
比方說 1/8 跟 1/16
$$
\dfrac{1}{8} -> \dfrac{0.5}{4} -> \dfrac{0.25}{2} -> \dfrac{0.125}{1}(答案)
$$
$$
\dfrac{1}{16} -> \dfrac{0.5}{8}->\dfrac{0.25}{4}->\dfrac{0.125}{2}->\dfrac{0.625}{1}(答案)
$$
...以此類推
因此當分母為 **1** 的時候就可以得到答案

問題是如果分母不是 2 的倍數時要怎麼處理？
像是 1/3 或者 1/5 或者是 1/10 ?

### 處理不是 2 的倍數的分母

#### 處理奇數
變成偶數，
如何計算誤差值 ?

#### 處理非 2 的倍數的分母


### 精準度
什麼時候程式結束?



### 思考
1. 如果 `orig / 2` 本身就是 double 除以 int 為什麼還要特別寫一個 divop 去處理浮點數除法 ?
    * 實驗: 拿一般浮點數除法跟 divop 去做比較 : 記憶體跟時間使用 

### reference:

- https://hackmd.io/@billsun/B1gii716N
- https://www.h-schmidt.net/FloatConverter/IEEE754.html
- https://hackmd.io/@sysprog/c-floating-point
- [Convert a Number from Decimal to IEEE 754 Floating Point Representation](https://www.wikihow.com/Convert-a-Number-from-Decimal-to-IEEE-754-Floating-Point-Representation)


## 測驗3

根據題目提供的公式
$$
kx = N - \dfrac{(k - 1)k}{2}  
$$
可以得到當以下式子為整數時可以找到一組連續數字表示 N
$$
x = \dfrac{N - \dfrac{(k - 1)k}{2}}{k}
$$
換成程式碼可以變成
```c=
if ((N - (k*(k-1)/2)) % k == 0)
   ret++;
```
剩下的就是 k 的範圍要怎麼跑
透過以下式子可以知道 k 的最大值
$$
k<√2N
$$
所以換成程式碼可以得到
```c=
   k < sqrt(2*N)
```
組合起來就會是以下這樣
```c=
    int ret = 1;
    double max = sqrt(2*N);
    for (int k = 2; k < max; k++ ){
        if ( (N - (k*(k-1)/2)) % k == 0)
           ret++;
    }
    return ret;
```
但是這樣子跟答案其實有很大的差異
```c=
int consecutiveNumbersSum(int N)
{
    if (N < 1)
        return 0;
    int ret = 1;

    int x = N;
    for (int k = 2; k < x; k++) {
    
        x += 1-k;
        
        if (x % k == 0)
            ret++;
    }
    return ret;
} 
```

To do .....