paizaラーニングレベルアップ問題集の「残り物の量」をやってみた

paiza×Qiita記事投稿キャンペーンということで、paizaラーニングレベルアップ問題集の[残り物の量](https://paiza.jp/works/mondai/c_rank_skillcheck_archive/leftover)をやってみました。
- - -
- サンプルプログラム（Python3）

    ```py
    m, p, q = map(int, input().split())
    m1 = m - m * p / 100
    m2 = m1 - m1 * q / 100
    print(m2)
    ```
    - まず、仕入れた生鮮食品の量$m\text{[kg]}$、生のまま売れた割合$p\text{[%]}$、売れ残った量のうちお惣菜にして売れた割合$q\text{[%]}$を受け取ります。
    - 次に、生のまま売れた量は$m\times\frac{p}{100}$、この時点で残った量は$m-m\times\frac{p}{100}$となります。この残った量を$m_1$とおきます。
    - $m_1\text{[kg]}$の食品をお惣菜に加工して売れた量は$m_1\times\frac{q}{100}$、この時点で残った量は$m_1-m_1\times\frac{q}{100}$となります。この残った量を$m_2$とおきます。
    - 問題文には$p=100$のとき$q=0$とありますが、これは特に気にしなくてよいです。
    - 最終的に売れ残った量$m_2$を出力します。
    　
    - 今回は問題になりませんが、この順番で計算するのは、誤差の累積が懸念されます。
    - 最初の売れ残りの量$m_1$を計算する時点で、人間は十進法で「$100$で割る」と書いたつもりでも、コンピュータは「$1100100_2$で割る」と処理するので、循環小数になってしまいます。
    - そこで、誤差の発生する計算（特に実数の除算）は最後にまとめてするようにし、誤差の発生を少なくすることを考えます。
    ```math
    \begin{split}
    m_2&=m_1-m_1\times\frac{q}{100}=m_1\times\left(1-\frac{q}{100}\right)\\
    &=\left(m-m\times\frac{p}{100}\right)\times\left(1-\frac{q}{100}\right)\\
    &=m\times\left(1-\frac{p}{100}\right)\times\left(1-\frac{q}{100}\right)\\
    &=m\times\frac{100-p}{100}\times\frac{100-q}{100}\\
    &=\frac{m(100-p)(100-q)}{10000}
    \end{split}
    ```
    より、
    ```py
    m, p, q = map(int, input().split())
    print(m * (100 - p) * (100 - q) / 10000)
    ```
    と書くことができます。
    最初に、整数部分`m*(100-p)*(100-q)`を計算して、最後に除算するようにしています。
- - -
- サンプルプログラム2（Python3）
言語によっては、`Decimal`等の十進数が用意されている言語もあります。今回のような入力が％単位で与えられる問題ならば、正確に計算することができます。
    ```py
    from decimal import Decimal
    m, p, q = map(Decimal, input().split())  # 十進数として受け取る
    m1 = m - m * p / 100
    m2 = m1 - m1 * q / 100
    print(m2)
    ```
    ※あくまで**十**進数ですので、「$3$で除算」のような、$2$と$5$以外を素因数に持つ整数の除算は正確にはできません。
- - -
- サンプルプログラム3（Python3）
    サンプルプログラム1の途中にある
    ```math
    m(100-p)(100-q)
    ```
    は、残った量の$10000$倍になっています。そこで、この値を$10000$で割った商が整数部分、余りが小数点以下四桁となりますので、書式指定をして
    ```py
    m, p, q = map(int, input().split())
    r = m * (100 - p) * (100 - q)
    print(f"{r // 10000}.{r % 10000:04d}")
    ```
    と書くこともできます。
    末尾に`0`が出ることが気になる方は
    ```py
    m, p, q = map(int, input().split())
    r = m * (100 - p) * (100 - q)
    print(f"{r // 10000}.{r % 10000:04d}".rstrip('0').rstrip('.'))
    ```
    としてもよいでしょう。

    勿論、普通に書式設定をして
    ```py
    print(f"{m * (100 - p) * (100 - q) / 10000:.4f}")
    ```
    或いは
    ```py
    print(f"{m * (100 - p) * (100 - q) / 10000:.4f}".rstrip('0').rstrip('.'))
    ```
    でも正解できます。
- - -
- サンプルプログラム4（C++）
    ```cpp
    #include <iostream>
    #include <iomanip>
    using namespace std;
    int main(void) {
    	int m, p, q;
    	cin >> m >> p >> q;
    	cout << setprecision(7) << (double) (m * (100 - p) * (100 - q)) / 10000 << endl;
    	return 0;
    }
    ```
    C++では、標準ライブラリヘッダ`iomanip`にある`setprecision`で出力桁数を指定しないと、6桁の精度でしか出力されず、不正解になるケースがあります。
    `(double)(m*(100-p)*(100-q))/10000`の代わりに`(m*(100-p)*(100-q))/10000.0`でもプログラム問題の解法としてはいいのですが、現実には$m$, $p$, $q$が整数で厳密に与えられることはなく、一方、`/10000`の部分は近似値ではないので整数で書きたいと思います。
    ```cpp
	#include <iostream>
	#include <iomanip>
	using namespace std;
	int main(void) {
		double m, p, q; //浮動小数点数として受け取る
		cin >> m >> p >> q;
		cout << fixed << setprecision(4) << (m * (100 - p) * (100 - q)) / 10000 << endl;
		return 0;
    }
    ```
    の方が現実に即していると言えるでしょう。