{ }codelper
小数の出力P0221

はかりに出る重さ

店のはかりは、物を載せたり下ろしたりするたびに、載っている重さの合計をキログラムで表示する。
はじめ、はかりには何も載っていない。
一行目に重さが変わった回数 n が与えられる。
続く n 行に、変わった重さ w が変わった順に一行に一つずつ与えられる。
w が正ならその分を載せ、負ならその分を下ろしたことを表す。
w は整数か、小数点以下 1〜3 桁まで書いた数である(例:2、0.5、-1.25、0.125、1.500)。
重さが変わるたびに、はかりが表示する合計を一行ずつ出力せよ。
はかりが表示する合計は、それまでに与えられた w をすべて正確に足した値である。
合計は小数点以下の末尾の 0 を消して書く(1.250 → 1.25)。
小数部分がすべて 0 なら小数点も書かない(2.000 → 2)。
合計が 1 より小さいときは小数点の前に 0 を書く(.5 ではなく 0.5)。
合計が 0 なら 0 と書く(-0 ではない)。
載っている重さより多くを下ろすことはないので、正確な合計はいつも 0 以上である。

入力
1 ≤ n ≤ 1,000、-100 ≤ w ≤ 100、w ≠ 0、w は小数点以下 0〜3 桁、正確な合計はいつも 0 以上
出力
n 行、各行にその時点の合計(末尾の 0 と残った小数点を消した形)
例 1 · stdin
3
0.1
0.2
-0.3
stdout
0.1
0.3
0
例 2 · stdin
5
2
0.5
-1.25
0.125
0.875
stdout
2
2.5
1.25
1.375
2.25
main.py
入力(stdin)

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

$ 準備完了

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

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

実数を足すと生じる小さな誤差を、人が読める形で出力する問題です。
0.1 と 0.2 は二進数で正確に表せないので、0.1 + 0.2 は 0.30000000000000004 になります。
そのまま出すと Python print(x) と JS・TS console.log(x) は 0.30000000000000004 を出し、Python と Java は 2 も 2.0 と出します。
そこでまず小数第三位までそろえた文字列を作ります:Python f"{x:.3f}"、C snprintf(buf, sizeof buf, "%.3f", x)、C++ std::fixed << std::setprecision(3)、Java String.format(Locale.ROOT, "%.3f", x)、C# x.ToString("F3", CultureInfo.InvariantCulture)。
次に末尾の 0 を消し、小数点だけが残ればそれも消します(Python .rstrip("0").rstrip(".")、C# .TrimEnd('0').TrimEnd('.'))。
0 だけを消すと 2.000 が 2. になります。
C の %g も末尾の 0 を消してくれますが、有効数字を 6 桁までしか書かないので 99999.999 を 100000 と出します。
二つ目の落とし穴は -0 です。
0.3 - 0.1 - 0.2 は 0 ではなく -0.0000000000000000277 なので、第三位まで出すと C・C++・Python・Java・C#(.NET Core 3.0 から)・JS の toFixed はどれも -0.000 になります。
消した結果が -0 なら 0 に直します(Python 3.11 からは f"{x:z.3f}" の z が -0 を 0 と出してくれます)。
JS・TS は Math.round(x * 1000) / 1000 で数をそろえてからそのまま出してもかまいません。
数を文字列にするとき末尾の 0 を書かず、-0 も 0 と書くからです。
Craykon コンソール.書く も末尾の 0 なしで出し、小数第九位まで四捨五入して出すので 0.1 + 0.2 も 0.3 と見せます。
ただし大きな重さを何百回も足して積もった誤差は隠しきれないので(99.999 を 491 回足すと 49099.509000001)、先に第三位にそろえます:数学.切り下げ(x * 1000 + 0.5) / 1000。
誤差そのものを避けたいなら、重さを 1000 倍した整数(グラム)で足す方法もあります。

Pythonmain.py · 11 行
n = int(input())total = 0.0out = []for _ in range(n):    total += float(input())    # 소수 셋째 자리까지 찍고 끝의 0과 남은 소수점을 지운다    s = f"{total:.3f}".rstrip("0").rstrip(".")    if s == "-0":          # 0 바로 아래의 작은 오차는 "-0.000"으로 찍힌다        s = "0"    out.append(s)print("\n".join(out))
JavaScriptmain.js · 11 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);let total = 0;const out = [];for (let i = 1; i <= n; i++) {  total += Number(lines[i]);  // 1000분의 1로 반올림한 수를 그대로 찍으면 끝의 0이 없다(2.5, 2). -0도 "0"으로 찍힌다  const shown = Math.round(total * 1000) / 1000;  out.push(String(shown));}console.log(out.join("\n"));
TypeScriptmain.ts · 12 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);let total = 0;const out: string[] = [];for (let i = 1; i <= n; i++) {  total += Number(lines[i]);  // 1000분의 1로 반올림한 수를 그대로 찍으면 끝의 0이 없다(2.5, 2). -0도 "0"으로 찍힌다  const shown: number = Math.round(total * 1000) / 1000;  out.push(String(shown));}console.log(out.join("\n"));
JavaMain.java · 22 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Locale; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(in.readLine().trim());        double total = 0;        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            total += Double.parseDouble(in.readLine().trim());            // Double.toString은 2.0처럼 찍으니 소수 셋째 자리로 찍고 끝의 0을 지운다            String s = String.format(Locale.ROOT, "%.3f", total);            s = s.replaceAll("0+$", "").replaceAll("\\.$", "");            if (s.equals("-0")) s = "0";            out.append(s).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 22 行
using System;using System.Globalization;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        double total = 0;        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            total += double.Parse(Console.ReadLine(), CultureInfo.InvariantCulture);            // "F3"으로 셋째 자리까지 찍고 끝의 0과 소수점을 지운다            string s = total.ToString("F3", CultureInfo.InvariantCulture).TrimEnd('0').TrimEnd('.');            if (s == "-0") s = "0";            sb.Append(s).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 24 行
#include <iomanip>#include <iostream>#include <sstream>#include <string> int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    double total = 0;    for (int i = 0; i < n; i++) {        double w;        std::cin >> w;        total += w;        std::ostringstream os;        os << std::fixed << std::setprecision(3) << total;        std::string s = os.str();        // 끝의 0과 남은 소수점을 지운다        while (s.back() == '0') s.pop_back();        if (s.back() == '.') s.pop_back();        if (s == "-0") s = "0";        std::cout << s << "\n";    }}
Cmain.c · 22 行
#include <stdio.h>#include <string.h> int main(void) {    int n;    double w, total = 0;    char buf[64];    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%lf", &w);        total += w;        snprintf(buf, sizeof buf, "%.3f", total);        /* 끝의 0을 지우고, 소수점만 남으면 그것도 지운다 */        size_t len = strlen(buf);        while (buf[len - 1] == '0') len--;        if (buf[len - 1] == '.') len--;        buf[len] = '\0';        if (strcmp(buf, "-0") == 0) strcpy(buf, "0");        puts(buf);    }    return 0;}
Craykonmain.cray · 9 行
n = コンソール.行読み().数に();total = 0;繰り返す (i = 0; i < n; i++){    total += コンソール.行読み().空白除去().数に();    // 1000분의 1 단위로 맞춘다. 쓰기는 끝의 0 없이 찍는다(2.5, 2)    shown = 数学.切り下げ(total * 1000 + 0.5) / 1000;    コンソール.書く(shown);}