{ }codelper
浮動小数点の誤差四捨五入P0131

預金の複利利息表

銀行にお金を預け、毎月利息を受け取る。
一行に、最初に預けた金額 P(ドル)、年利 R(パーセント)、月数 m が空白で区切って与えられる。
P と R は小数点以下をちょうど二桁で書いた数である(例:1000.00、3.25)。
毎月末に、その月の利息を 残高 × R ÷ 100 ÷ 12 で計算する。
利息はセント(0.01 ドル)単位に四捨五入し、セント未満がちょうど 0.5 セントなら切り上げる。
四捨五入した利息を残高に加え、翌月の利息は増えた残高で計算する。
1 か月目から m か月目まで、一か月ごとに一行ずつ「月の番号(1 から m)、その月の利息、利息を加えた残高」を空白一つで区切って出力せよ。
最後の行には m か月の間に受け取った利息の合計を出力せよ。
金額はすべて小数点以下をちょうど二桁で書く(例:5.00、0.03)。

入力
0.01 ≤ P ≤ 1,000,000.00、0.00 ≤ R ≤ 20.00、1 ≤ m ≤ 120
出力
m 行:月、利息、残高 / 最後の行:利息の合計(金額は小数第二位まで)
例 1 · stdin
1000.00 6.00 3
stdout
1 5.00 1005.00
2 5.03 1010.03
3 5.05 1015.08
15.08
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

お金の計算は実数(double)ではなく整数のセントで行うのが正解です。
0.01 や 1005.03 のような小数は二進数で正確に表せないので、float/double で 120 か月足して四捨五入すると、どこかの月で 1 セントずれることがあります。
そこで入力の文字列 "1000.05" を点で分けて整数 100005 セントとして読みます:Python s.split(".")、JS・TS s.split(".")、Java s.split("\\.")(正規表現なので点をエスケープ)、C# s.Split('.')、C scanf("%lld.%lld")、C++ s.find('.') と std::stoll、Craykon 行.置き換え(".", "").数に() です。
すると正確な利息は 残高 × 利率 ÷ 120000 セントで、0.5 を切り上げる四捨五入は整数だけで (2 × 残高 × 利率 + 120000) ÷ 240000 の商です。
丸め関数の落とし穴:Python round と C# Math.Round は既定が偶数への丸め(銀行丸め)なので、252.5 が 252 になります。
C# Math.Round(x, MidpointRounding.AwayFromZero)、Java Math.round、JS・TS Math.round、C・C++ round、Craykon 数学.四捨五入 は 0.5 を切り上げます(負の数の規則は言語ごとに違います)。
それでもこれらの関数は実数を受け取るので、ちょうど 0.5 かどうかを知るには整数で計算するほうが安全です。
ただの整数除算 残高 * 利率 / 120000 だけだと、四捨五入ではなく切り捨てになります。
大きさにも注意します。
2 × 残高 × 利率は 3×10^12 まで大きくなり 32 ビット int を超えるので、Java・C# は long、C・C++ は long long です。
Python の整数には制限がなく、JS・TS・Craykon の数は 2^53 まで整数が正確なので大丈夫です。
出力は商と余りから "5.03" を作り、余りは前に 0 を詰めて二桁にします:Python f"{c % 100:02d}"、JS・TS padStart(2, "0")、Java String.format("%02d")、C# :D2、C %02lld、Craykon 前埋め(2, "0")。

Pythonmain.py · 21 行
def hundredths(s):    # "1000.05" → 100005 (소수점 아래는 언제나 두 자리)    whole, frac = s.split(".")    return int(whole) * 100 + int(frac)  def money(c):    return f"{c // 100}.{c % 100:02d}"  p, r, m = input().split()bal = hundredths(p)  # 잔액(센트)rate = hundredths(r)  # 연 이율(0.01% 단위): 3.25% → 325total = 0for month in range(1, int(m) + 1):    # 정확한 이자 = bal × rate / 120000 센트. 반올림(0.5는 올림) = (2 × 분자 + 분모) // (2 × 분모)    interest = (2 * bal * rate + 120000) // 240000    bal += interest    total += interest    print(month, money(interest), money(bal))print(money(total))
JavaScriptmain.js · 20 行
const [p, r, m] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);// "1000.05" → 100005 (소수점 아래는 언제나 두 자리). 정수로만 계산하면 실수 오차가 없다const hundredths = (s) => {  const [whole, frac] = s.split(".");  return Number(whole) * 100 + Number(frac);};const money = (c) => Math.floor(c / 100) + "." + String(c % 100).padStart(2, "0");let bal = hundredths(p); // 잔액(센트)const rate = hundredths(r); // 연 이율(0.01% 단위)let total = 0;const out = [];for (let month = 1; month <= Number(m); month++) {  // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 최대 3×10^12라 number로 정확하다  const interest = Math.floor((2 * bal * rate + 120000) / 240000);  bal += interest;  total += interest;  out.push(month + " " + money(interest) + " " + money(bal));}out.push(money(total));console.log(out.join("\n"));
TypeScriptmain.ts · 21 行
import { readFileSync } from "node:fs";const [p, r, m]: string[] = readFileSync(0, "utf8").trim().split(/\s+/);// "1000.05" → 100005 (소수점 아래는 언제나 두 자리). 정수로만 계산하면 실수 오차가 없다const hundredths = (s: string): number => {  const [whole, frac] = s.split(".");  return Number(whole) * 100 + Number(frac);};const money = (c: number): string => Math.floor(c / 100) + "." + String(c % 100).padStart(2, "0");let bal: number = hundredths(p); // 잔액(센트)const rate: number = hundredths(r); // 연 이율(0.01% 단위)let total = 0;const out: string[] = [];for (let month = 1; month <= Number(m); month++) {  // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 최대 3×10^12라 number로 정확하다  const interest: number = Math.floor((2 * bal * rate + 120000) / 240000);  bal += interest;  total += interest;  out.push(month + " " + money(interest) + " " + money(bal));}out.push(money(total));console.log(out.join("\n"));
JavaMain.java · 31 行
import java.util.Scanner; public class Main {    // "1000.05" → 100005 (소수점 아래는 언제나 두 자리)    static long hundredths(String s) {        String[] parts = s.split("\\.");        return Long.parseLong(parts[0]) * 100 + Long.parseLong(parts[1]);    }     static String money(long c) {        return c / 100 + "." + String.format("%02d", c % 100);    }     public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        long bal = hundredths(sc.next()); // 잔액(센트)        long rate = hundredths(sc.next()); // 연 이율(0.01% 단위)        int m = sc.nextInt();        long total = 0;        StringBuilder out = new StringBuilder();        for (int month = 1; month <= m; month++) {            // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 곱이 int를 넘으니 long            long interest = (2 * bal * rate + 120000) / 240000;            bal += interest;            total += interest;            out.append(month).append(' ').append(money(interest)).append(' ').append(money(bal)).append('\n');        }        out.append(money(total)).append('\n');        System.out.print(out);    }}
C#Main.cs · 34 行
using System;using System.Text; class Program{    // "1000.05" → 100005 (소수점 아래는 언제나 두 자리)    static long Hundredths(string s)    {        string[] parts = s.Split('.');        return long.Parse(parts[0]) * 100 + long.Parse(parts[1]);    }     static string Money(long c) => $"{c / 100}.{c % 100:D2}";     static void Main()    {        string[] t = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        long bal = Hundredths(t[0]); // 잔액(센트)        long rate = Hundredths(t[1]); // 연 이율(0.01% 단위)        int m = int.Parse(t[2]);        long total = 0;        var sb = new StringBuilder();        for (int month = 1; month <= m; month++)        {            // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. Math.Round는 기본이 짝수 쪽이라 쓰지 않는다            long interest = (2 * bal * rate + 120000) / 240000;            bal += interest;            total += interest;            sb.Append(month).Append(' ').Append(Money(interest)).Append(' ').Append(Money(bal)).Append('\n');        }        sb.Append(Money(total)).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 31 行
#include <iomanip>#include <iostream>#include <string> // "1000.05" → 100005 (소수점 아래는 언제나 두 자리)long long hundredths(const std::string& s) {    auto dot = s.find('.');    return std::stoll(s.substr(0, dot)) * 100 + std::stoll(s.substr(dot + 1));} std::string money(long long c) {    std::string cents = std::to_string(c % 100);    return std::to_string(c / 100) + "." + (cents.size() < 2 ? "0" : "") + cents;} int main() {    std::string p, r;    int m;    std::cin >> p >> r >> m;    long long bal = hundredths(p);  // 잔액(센트)    long long rate = hundredths(r); // 연 이율(0.01% 단위)    long long total = 0;    for (int month = 1; month <= m; month++) {        // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 곱이 int를 넘으니 long long        long long interest = (2 * bal * rate + 120000) / 240000;        bal += interest;        total += interest;        std::cout << month << ' ' << money(interest) << ' ' << money(bal) << '\n';    }    std::cout << money(total) << '\n';}
Cmain.c · 34 行
#include <stdio.h> /* "1000.05"를 정수 1000과 05로 따로 읽는다 — 소수는 언제나 두 자리다 */long long read_hundredths(void) {    long long whole, frac;    scanf("%lld.%lld", &whole, &frac);    return whole * 100 + frac;} void print_money(long long c) {    printf("%lld.%02lld", c / 100, c % 100);} int main(void) {    long long bal = read_hundredths();  /* 잔액(센트) */    long long rate = read_hundredths(); /* 연 이율(0.01% 단위) */    int m;    scanf("%d", &m);    long long total = 0;    for (int month = 1; month <= m; month++) {        /* 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 곱이 int를 넘으니 long long */        long long interest = (2 * bal * rate + 120000) / 240000;        bal += interest;        total += interest;        printf("%d ", month);        print_money(interest);        putchar(' ');        print_money(bal);        putchar('\n');    }    print_money(total);    putchar('\n');    return 0;}
Craykonmain.cray · 25 行
백분(줄){    // "1000.05" → "100005" → 100005 (소수점 아래는 언제나 두 자리)    返す 줄.置き換え(".", "").数に();} 돈(c){    返す 数学.商(c, 100) + "." + ("" + c % 100).前埋め(2, "0");} t = コンソール.行読み().分ける(" ");bal = 백분(t[0]); // 잔액(센트)rate = 백분(t[1]); // 연 이율(0.01% 단위)m = t[2].数に();total = 0;繰り返す (month = 1; month <= m; month++){    // 정확한 이자 = bal × rate / 120000 센트, 0.5는 올림. 최대 3×10^12라 수가 정확하다    interest = 数学.商(2 * bal * rate + 120000, 240000);    bal += interest;    total += interest;    コンソール.書く(month + " " + 돈(interest) + " " + 돈(bal));}コンソール.書く(돈(total));