{ }codelper
절댓값실수 오차P0134

부품 치수 공차 검사

공장에서 만든 부품의 길이를 재서 도면의 기준 길이와 비교한다. 첫 줄에 기준 길이 N과 공차 T가 빈칸으로 나뉘어 주어진다(단위 mm). 둘째 줄에 부품 수 n이 주어지고, 다음 n줄에 1번 부품부터 차례로 잰 길이가 하나씩 주어진다. 모든 길이와 공차는 소수점 아래를 정확히 세 자리로 쓴 수다(예: 25.400, 0.050). 부품의 차이는 (잰 길이 - N)이다. 차이의 크기(절댓값)가 T 이하이면 합격, T보다 크면 불합격이다. 먼저 부품마다 차이를 한 줄에 하나씩 출력하라. 차이는 소수점 아래 세 자리로 쓰고, 0보다 크면 앞에 "+", 0보다 작으면 "-"를 붙이며, 0이면 부호 없이 0.000으로 쓴다. 그다음 줄에 합격한 부품의 수를 출력하라. 그다음 줄에 불합격한 부품의 번호를 작은 것부터 빈칸 하나씩 띄어 출력하고, 없으면 "없음"을 출력하라. 마지막 줄에 차이의 크기가 가장 큰 부품의 번호를 출력하라(같으면 번호가 작은 것).

입력
0.001 ≤ N ≤ 1000.000, 0.000 ≤ T ≤ 10.000, 1 ≤ n ≤ 1,000, 0.000 ≤ 잰 길이 ≤ 2000.000
출력
n줄의 차이, 합격 수, 불합격 번호들(또는 "없음"), 차이가 가장 큰 번호
예시 1 · stdin
25.400 0.050
5
25.412
25.450
25.351
25.400
25.349
stdout
+0.012
+0.050
-0.049
0.000
-0.051
4
5
5
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

경계에 딱 놓인 부품이 함정이에요.
25.450과 25.400을 실수(double)로 읽어 빼면 0.05000000000000071이 되어 0.05보다 커지고, 합격이어야 할 부품이 떨어져요.
0.05·25.45 같은 소수는 이진수로 정확히 적을 수 없어서, 여덟 언어 모두 double이라면 같은 일이 생겨요(크레이콘의 수도 JS와 같은 double이에요).
해법은 소수점 아래가 늘 세 자리라는 약속을 써서 1/1000mm 단위 정수로 읽는 거예요: 점을 지우고 정수로 바꾸면 "25.450"은 25450이에요(Python int(s.replace(".", "")), JS·TS Number(s.replace(".", "")), Java Integer.parseInt(s.replace(".", "")), C# int.Parse(s.Replace(".", "")), C scanf("%d.%d"), C++ std::stoi, 크레이콘 줄.바꾸기(".", "").수로변환()).
절댓값은 Python abs, JS·TS Math.abs, Java Math.abs, C# Math.Abs, C abs(<stdlib.h>, int용), C++ std::abs, 크레이콘 수학.절댓값이에요.
C에서 실수 절댓값은 fabs(<math.h>)라는 다른 함수예요.
절댓값을 빼고 d <= T로 물으면 기준보다 작은 부품은 얼마나 작든 다 합격해요.
음수 차이를 찍을 때 둘째 함정이 있어요.
-50(= -0.050)을 그대로 1000으로 나누면 C·C++·Java·C#·JS(Math.trunc)는 0 쪽으로 버려 몫이 0이 되어 부호가 사라지고, Python //는 내림이라 몫이 -1, % 나머지가 950이 되어 "-1.950"이 돼요(JS Math.floor도 몫이 -1이지만 %는 -50이라 또 다르게 틀려요).
그래서 부호를 먼저 떼어 쓰고, 절댓값을 몫과 나머지로 나눠 나머지는 세 자리로 앞에 0을 채워요.

Pythonmain.py · 30 줄
def milli(s):    # "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다    return int(s.replace(".", ""))  def signed(d):    # 부호를 따로 떼고 절댓값으로 자리를 나눈다(-50 // 1000은 -1이라 그대로 나누면 틀린다)    sign = "+" if d > 0 else "-" if d < 0 else ""    a = abs(d)    return f"{sign}{a // 1000}.{a % 1000:03d}"  nominal, tol = map(milli, input().split())n = int(input())passed = 0failed = []worst = 1worst_dev = -1for k in range(1, n + 1):    d = milli(input().strip()) - nominal    print(signed(d))    if abs(d) <= tol:        passed += 1    else:        failed.append(k)    if abs(d) > worst_dev:  # 같으면 앞 번호를 둔다        worst, worst_dev = k, abs(d)print(passed)print(" ".join(map(str, failed)) if failed else "없음")print(worst)
JavaScriptmain.js · 27 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");// "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다const milli = (s) => Number(s.trim().replace(".", ""));const signed = (d) => {  const sign = d > 0 ? "+" : d < 0 ? "-" : "";  const a = Math.abs(d); // 부호를 떼고 자리를 나눈다  return sign + Math.floor(a / 1000) + "." + String(a % 1000).padStart(3, "0");};const [nominal, tol] = lines[0].trim().split(/\s+/).map(milli);const n = Number(lines[1]);const out = [];let passed = 0;const failed = [];let worst = 1;let worstDev = -1;for (let k = 1; k <= n; k++) {  const d = milli(lines[1 + k]) - nominal;  out.push(signed(d));  if (Math.abs(d) <= tol) passed++;  else failed.push(k);  if (Math.abs(d) > worstDev) {    worst = k; // 같으면 앞 번호를 둔다    worstDev = Math.abs(d);  }}out.push(String(passed), failed.length ? failed.join(" ") : "없음", String(worst));console.log(out.join("\n"));
TypeScriptmain.ts · 28 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");// "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다const milli = (s: string): number => Number(s.trim().replace(".", ""));const signed = (d: number): string => {  const sign = d > 0 ? "+" : d < 0 ? "-" : "";  const a = Math.abs(d); // 부호를 떼고 자리를 나눈다  return sign + Math.floor(a / 1000) + "." + String(a % 1000).padStart(3, "0");};const [nominal, tol] = lines[0].trim().split(/\s+/).map(milli);const n = Number(lines[1]);const out: string[] = [];let passed = 0;const failed: number[] = [];let worst = 1;let worstDev = -1;for (let k = 1; k <= n; k++) {  const d = milli(lines[1 + k]) - nominal;  out.push(signed(d));  if (Math.abs(d) <= tol) passed++;  else failed.push(k);  if (Math.abs(d) > worstDev) {    worst = k; // 같으면 앞 번호를 둔다    worstDev = Math.abs(d);  }}out.push(String(passed), failed.length ? failed.join(" ") : "없음", String(worst));console.log(out.join("\n"));
JavaMain.java · 39 줄
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    // "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다    static int milli(String s) {        return Integer.parseInt(s.trim().replace(".", ""));    }     static String signed(int d) {        String sign = d > 0 ? "+" : d < 0 ? "-" : "";        int a = Math.abs(d); // 부호를 떼고 자리를 나눈다        return sign + a / 1000 + "." + String.format("%03d", a % 1000);    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split("\\s+");        int nominal = milli(first[0]), tol = milli(first[1]);        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        StringBuilder failed = new StringBuilder();        int passed = 0, worst = 1, worstDev = -1;        for (int k = 1; k <= n; k++) {            int d = milli(in.readLine()) - nominal;            out.append(signed(d)).append('\n');            if (Math.abs(d) <= tol) passed++;            else failed.append(failed.length() > 0 ? " " : "").append(k);            if (Math.abs(d) > worstDev) { // 같으면 앞 번호를 둔다                worst = k;                worstDev = Math.abs(d);            }        }        out.append(passed).append('\n');        out.append(failed.length() > 0 ? failed.toString() : "없음").append('\n');        out.append(worst).append('\n');        System.out.print(out);    }}
C#Main.cs · 42 줄
using System;using System.Collections.Generic;using System.Text; class Program{    // "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다    static int Milli(string s) => int.Parse(s.Trim().Replace(".", ""));     static string Signed(int d)    {        string sign = d > 0 ? "+" : d < 0 ? "-" : "";        int a = Math.Abs(d); // 부호를 떼고 자리를 나눈다        return $"{sign}{a / 1000}.{a % 1000:D3}";    }     static void Main()    {        string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int nominal = Milli(first[0]), tol = Milli(first[1]);        int n = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        var failed = new List<int>();        int passed = 0, worst = 1, worstDev = -1;        for (int k = 1; k <= n; k++)        {            int d = Milli(Console.ReadLine()) - nominal;            sb.Append(Signed(d)).Append('\n');            if (Math.Abs(d) <= tol) passed++;            else failed.Add(k);            if (Math.Abs(d) > worstDev) // 같으면 앞 번호를 둔다            {                worst = k;                worstDev = Math.Abs(d);            }        }        sb.Append(passed).Append('\n');        sb.Append(failed.Count > 0 ? string.Join(" ", failed) : "없음").Append('\n');        sb.Append(worst).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 37 줄
#include <cstdlib>#include <iomanip>#include <iostream>#include <string>#include <vector> // "25.400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다int milli(const std::string& s) {    auto dot = s.find('.');    return std::stoi(s.substr(0, dot)) * 1000 + std::stoi(s.substr(dot + 1));} int main() {    std::string a, b;    int n;    std::cin >> a >> b >> n;    int nominal = milli(a), tol = milli(b);    std::vector<int> failed;    int passed = 0, worst = 1, worstDev = -1;    for (int k = 1; k <= n; k++) {        std::string s;        std::cin >> s;        int d = milli(s) - nominal;        int dev = std::abs(d); // 부호를 떼고 자리를 나눈다        std::cout << (d > 0 ? "+" : d < 0 ? "-" : "") << dev / 1000 << '.' << std::setw(3) << std::setfill('0') << dev % 1000 << '\n';        if (dev <= tol) passed++;        else failed.push_back(k);        if (dev > worstDev) { // 같으면 앞 번호를 둔다            worst = k;            worstDev = dev;        }    }    std::cout << passed << '\n';    if (failed.empty()) std::cout << "없음";    for (size_t i = 0; i < failed.size(); i++) std::cout << (i ? " " : "") << failed[i];    std::cout << '\n' << worst << '\n';}
Cmain.c · 35 줄
#include <stdio.h>#include <stdlib.h> /* "25.400"을 정수 25와 400으로 따로 읽는다 — 소수는 언제나 세 자리라 400은 1/1000 단위다 */int read_milli(void) {    int whole, frac;    scanf("%d.%d", &whole, &frac);    return whole * 1000 + frac;} int main(void) {    int nominal = read_milli();    int tol = read_milli();    int n;    scanf("%d", &n);    static int failed[1000];    int nfailed = 0, passed = 0, worst = 1, worst_dev = -1;    for (int k = 1; k <= n; k++) {        int d = read_milli() - nominal;        int a = abs(d); /* 정수 절댓값(실수라면 fabs) */        /* 부호를 따로 쓰고 절댓값으로 자리를 나눈다 */        printf("%s%d.%03d\n", d > 0 ? "+" : d < 0 ? "-" : "", a / 1000, a % 1000);        if (a <= tol) passed++;        else failed[nfailed++] = k;        if (a > worst_dev) { /* 같으면 앞 번호를 둔다 */            worst = k;            worst_dev = a;        }    }    printf("%d\n", passed);    if (nfailed == 0) printf("없음");    for (int i = 0; i < nfailed; i++) printf(i ? " %d" : "%d", failed[i]);    printf("\n%d\n", worst);    return 0;}
Craykonmain.cray · 57 줄
천분(줄){    // "25.400" → "25400" → 25400 (소수점 아래는 언제나 세 자리): 실수로 바꾸지 않아 오차가 없다    돌려주기 줄.공백제거().바꾸기(".", "").수로변환();} 부호붙이기(d){    부호 = "";    만약 (d > 0)    {        부호 = "+";    }    아니면 만약 (d < 0)    {        부호 = "-";    }    a = 수학.절댓값(d); // 부호를 떼고 자리를 나눈다    돌려주기 부호 + 수학.몫(a, 1000) + "." + ("" + a % 1000).앞채우기(3, "0");} first = 콘솔.줄읽기().나누기();nominal = 천분(first[0]);tol = 천분(first[1]);n = 콘솔.줄읽기().수로변환();passed = 0;failed = [];worst = 1;worstDev = -1;반복 (k = 1; k <= n; k++){    d = 천분(콘솔.줄읽기()) - nominal;    콘솔.쓰기(부호붙이기(d));    만약 (수학.절댓값(d) <= tol)    {        passed += 1;    }    아니면    {        failed.넣기(k);    }    만약 (수학.절댓값(d) > worstDev)    {        worst = k; // 같으면 앞 번호를 둔다        worstDev = 수학.절댓값(d);    }}콘솔.쓰기(passed);만약 (failed.개수 > 0){    콘솔.쓰기(failed);}아니면{    콘솔.쓰기("없음");}콘솔.쓰기(worst);