{ }codelper
타입 바꾸기P0397

작은 타입에 옮겨 담기

정수 하나를 크기가 정해진 정수 타입에 차례로 옮겨 담는다. 타입은 i8, u8, i16, u16, i32, u32 여섯 가지다. i로 시작하면 부호 있는 타입, u로 시작하면 부호 없는 타입이고, 뒤의 수 k는 비트 수다. k비트 부호 있는 타입에는 -2^(k-1) 이상 2^(k-1) - 1 이하의 정수가 들어간다. k비트 부호 없는 타입에는 0 이상 2^k - 1 이하의 정수가 들어간다. 옮겨 담는 방법은 두 가지다. 잘라 담기는 타입 이름만 쓴다(i8).
그 타입에 들어가는 정수 가운데 옮겨 담을 값과의 차가 2^k의 배수인 수는 하나뿐이고, 그 수가 결과다. 맞춰 담기는 타입 이름 뒤에 !
를 붙여 쓴다(i8!).
옮겨 담을 값이 그 타입에 들어가면 그대로, 가장 작은 수보다 작으면 가장 작은 수, 가장 큰 수보다 크면 가장 큰 수가 결과다. 첫 줄에 줄 수 q가 주어진다. 다음 q줄에 정수 v와 단계들이 주어진다.
단계 하나는 옮겨 담는 방법 하나다. 첫 단계는 v를 옮겨 담고, 둘째 단계부터는 바로 앞 단계의 결과를 옮겨 담는다. 줄마다 각 단계의 결과를 차례로 빈칸 하나로 띄워 출력한다. 마지막 줄에는 결과가 옮겨 담기 전의 값과 다른 단계가 모든 줄을 통틀어 몇 개인지 출력한다.

입력
1 ≤ q ≤ 1000.
-9000000000000000 ≤ v ≤ 9000000000000000.
한 줄의 단계는 1개 이상 8개 이하다.
v와 단계들은 빈칸 하나로 띄워 주어진다
출력
q줄, 그리고 마지막 줄에 값이 바뀐 단계의 수
예시 1 · stdin
4
200 i8 u16 u32 i8!
-1 u32 i8 u8 u8!
300 u8! i8 i16
128 i8 u8 i8!
stdout
-56 65480 65480 127
4294967295 -1 255 255
255 -1 -1
-128 128 127
11
main.py
입력(stdin)

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

$ 준비됐어요

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

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

정수를 더 작은 타입에 넣을 때 하는 일이 언어마다 달라요.
C는 (int8_t)v, C++은 static_cast<std::uint16_t>(v)처럼 크기가 정해진 타입으로 캐스트하면 아래쪽 비트만 남아요.
다만 C 표준은 부호 있는 타입에 들어가지 않는 값을 넣은 결과를 컴파일러가 정하도록 맡겨 두었어요(정의되지 않은 동작은 아니에요).
gcc와 clang은 아래쪽 비트만 남기고, C++20부터는 표준이 그렇게 정해요.
Java도 (byte)v, (short)v, (int)v가 같은 일을 하지만 부호 없는 타입은 16비트 char뿐이라서 u8은 v & 0xFF, u32는 v & 0xFFFFFFFFL로 잘라요.
C#은 sbyte, byte, short, ushort, int, uint가 모두 있고 unchecked 안에서 (sbyte)v처럼 캐스트해요.
JS는 수가 한 가지뿐이지만 비트 연산이 값을 먼저 32비트로 자르기 때문에 (v << 24) >> 24(i8), v & 0xff(u8), v | 0(i32), v >>> 0(u32)으로 써요.
TS 풀이는 Int8Array 같은 타입 배열의 칸에 넣었다가 꺼내요.
Python 정수는 크기에 끝이 없어 저절로 잘리지 않으니 v % size로 직접 계산해요(size가 양수면 Python의 %는 늘 0 이상이에요).
크레이콘도 정수 타입이 따로 없어 %로 계산해요.
맞춰 담기는 어느 풀이든 넓은 타입인 채로 두 끝과 견줘요(C# Math.Clamp, C++ std::clamp).
함정 하나: 크레이콘의 %는 왼쪽 수의 부호를 따라서 -56 % 65536이 -56이에요.
음수이면 2^k를 더해야 해요.
빼먹으면 예시 첫 줄이 -56 -56 -56 -56이 돼요.
함정 둘: 부호 있는 타입에서 딱 2^(k-1)인 값은 범위 밖이에요.
i8에 128을 잘라 담으면 -128이에요.
경계를 >로 견주면 예시 넷째 줄이 128 128 127이 돼요.
함정 셋: 단계는 이어져요.
200을 i8에 담은 -56을 u16에 담으면 65480이에요.
단계마다 처음 값을 담으면 예시 첫 줄이 -56 200 200 127이 돼요.
함정 넷: Java에서 v & 0xFFFFFFFF처럼 L을 빼면 마스크가 int -1이고, long으로 넓어져도 -1이라서 아무것도 잘리지 않아요.
예시 둘째 줄이 -1 -1 255 255가 돼요.
함정 다섯: JS에서 v & 0xffffffff의 결과는 부호 있는 32비트 수라서 역시 둘째 줄이 -1 -1 255 255가 돼요.
부호 없는 결과는 >>> 0만 줘요.
함정 여섯: C++에서 std::int8_t, std::uint8_t 값을 그대로 std::cout에 넘기면 수가 아니라 그 코드의 글자가 찍혀요.
예시 첫 줄의 -56 자리에 바이트 하나가 나와요.
long long으로 되돌려서 써요.
함정 일곱: C#의 Convert.ToSByte(v)는 자르지 않고 범위를 벗어나면 OverflowException을 던져요.
예시 첫 줄의 200에서 멈춰요.

Pythonmain.py · 34 줄
import sys  def convert(v, step):    signed = step[0] == "i"    bits = int(step[1:].rstrip("!"))    size = 1 << bits                      # 2^bits    lo = -(size // 2) if signed else 0    hi = lo + size - 1    if step.endswith("!"):                # 끝값에 맞춰 담기        return max(lo, min(hi, v))    r = v % size                          # Python의 %는 늘 0 이상 size 미만    if r > hi:                            # 부호 있는 타입의 위쪽 절반은 음수        r -= size    return r  lines = sys.stdin.read().split("\n")q = int(lines[0])out = []changed = 0for line in lines[1:q + 1]:    parts = line.split()    v = int(parts[0])    row = []    for step in parts[1:]:        nv = convert(v, step)             # 바로 앞 단계의 결과를 다시 옮겨 담는다        if nv != v:            changed += 1        v = nv        row.append(str(v))    out.append(" ".join(row))out.append(str(changed))print("\n".join(out))
JavaScriptmain.js · 43 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]); const RANGE = {  i8: [-128, 127], u8: [0, 255],  i16: [-32768, 32767], u16: [0, 65535],  i32: [-2147483648, 2147483647], u32: [0, 4294967295],}; // 비트 연산은 먼저 값을 32비트로 자른 뒤에 한다function wrap(v, type) {  switch (type) {    case "i8": return (v << 24) >> 24;   // 아래 8비트를 맨 위로 올렸다가 부호를 채우며 내린다    case "u8": return v & 0xff;    case "i16": return (v << 16) >> 16;    case "u16": return v & 0xffff;    case "i32": return v | 0;    default: return v >>> 0;             // u32: >>>만 결과를 부호 없는 수로 준다  }} const out = [];let changed = 0;for (let i = 1; i <= q; i++) {  const parts = lines[i].trim().split(" ");  let v = Number(parts[0]);  const row = [];  for (const step of parts.slice(1)) {    let nv;    if (step.endsWith("!")) {      const [lo, hi] = RANGE[step.slice(0, -1)];      nv = Math.max(lo, Math.min(hi, v));    } else {      nv = wrap(v, step);    }    if (nv !== v) changed++;    v = nv;    row.push(v);  }  out.push(row.join(" "));}out.push(changed);console.log(out.join("\n"));
TypeScriptmain.ts · 47 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]); type IntType = "i8" | "u8" | "i16" | "u16" | "i32" | "u32"; const RANGE: Record<IntType, [number, number]> = {  i8: [-128, 127], u8: [0, 255],  i16: [-32768, 32767], u16: [0, 65535],  i32: [-2147483648, 2147483647], u32: [0, 4294967295],}; // 타입 배열의 칸에 넣으면 그 칸의 크기에 맞게 잘려 들어간다const CELL: Record<IntType, Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array> = {  i8: new Int8Array(1), u8: new Uint8Array(1),  i16: new Int16Array(1), u16: new Uint16Array(1),  i32: new Int32Array(1), u32: new Uint32Array(1),}; function wrap(v: number, type: IntType): number {  const cell = CELL[type];  cell[0] = v;  return cell[0];} const out: string[] = [];let changed = 0;for (let i = 1; i <= q; i++) {  const parts: string[] = lines[i].trim().split(" ");  let v = Number(parts[0]);  const row: number[] = [];  for (const step of parts.slice(1)) {    let nv: number;    if (step.endsWith("!")) {      const [lo, hi] = RANGE[step.slice(0, -1) as IntType];      nv = Math.max(lo, Math.min(hi, v));    } else {      nv = wrap(v, step as IntType);    }    if (nv !== v) changed++;    v = nv;    row.push(v);  }  out.push(row.join(" "));}out.push(String(changed));console.log(out.join("\n"));
JavaMain.java · 44 줄
import java.io.*; public class Main {    // 잘라 담기: Java의 정수 캐스트는 아래쪽 비트만 남긴다    static long wrap(long v, String type) {        switch (type) {            case "i8": return (byte) v;            case "u8": return v & 0xFF;              // Java에는 부호 없는 byte가 없다            case "i16": return (short) v;            case "u16": return (char) v;             // char는 부호 없는 16비트            case "i32": return (int) v;            default: return v & 0xFFFFFFFFL;         // u32: L을 빼면 0xFFFFFFFF는 int -1이다        }    }     static long clamp(long v, String type) {        int bits = Integer.parseInt(type.substring(1));        long lo = type.charAt(0) == 'i' ? -(1L << (bits - 1)) : 0;        long hi = lo + (1L << bits) - 1;        return Math.max(lo, Math.min(hi, v));    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        int changed = 0;        for (int i = 0; i < q; i++) {            String[] parts = in.readLine().trim().split(" ");            long v = Long.parseLong(parts[0]);            for (int j = 1; j < parts.length; j++) {                String step = parts[j];                long nv = step.endsWith("!") ? clamp(v, step.substring(0, step.length() - 1)) : wrap(v, step);                if (nv != v) changed++;                v = nv;                if (j > 1) out.append(' ');                out.append(v);            }            out.append('\n');        }        out.append(changed).append('\n');        System.out.print(out);    }}
C#Main.cs · 59 줄
using System;using System.Text; class Program{    // 잘라 담기: unchecked 캐스트는 아래쪽 비트만 남긴다    static long Wrap(long v, string type)    {        unchecked        {            switch (type)            {                case "i8": return (sbyte)v;                case "u8": return (byte)v;                case "i16": return (short)v;                case "u16": return (ushort)v;                case "i32": return (int)v;                default: return (uint)v;            }        }    }     static long Clamp(long v, string type)    {        switch (type)        {            case "i8": return Math.Clamp(v, sbyte.MinValue, sbyte.MaxValue);            case "u8": return Math.Clamp(v, byte.MinValue, byte.MaxValue);            case "i16": return Math.Clamp(v, short.MinValue, short.MaxValue);            case "u16": return Math.Clamp(v, ushort.MinValue, ushort.MaxValue);            case "i32": return Math.Clamp(v, int.MinValue, int.MaxValue);            default: return Math.Clamp(v, uint.MinValue, uint.MaxValue);        }    }     static void Main()    {        int q = int.Parse(Console.ReadLine());        var output = new StringBuilder();        int changed = 0;        for (int i = 0; i < q; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            long v = long.Parse(parts[0]);            for (int j = 1; j < parts.Length; j++)            {                string step = parts[j];                long nv = step.EndsWith("!") ? Clamp(v, step.TrimEnd('!')) : Wrap(v, step);                if (nv != v) changed++;                v = nv;                if (j > 1) output.Append(' ');                output.Append(v);            }            output.Append('\n');        }        output.Append(changed).Append('\n');        Console.Write(output);    }}
C++main.cpp · 53 줄
#include <algorithm>#include <cstdint>#include <iostream>#include <sstream>#include <string> // 잘라 담기: static_cast로 크기가 정해진 타입에 넣으면 아래쪽 비트만 남는다long long wrap(long long v, const std::string& type) {    if (type == "i8") return static_cast<std::int8_t>(v);    if (type == "u8") return static_cast<std::uint8_t>(v);    if (type == "i16") return static_cast<std::int16_t>(v);    if (type == "u16") return static_cast<std::uint16_t>(v);    if (type == "i32") return static_cast<std::int32_t>(v);    return static_cast<std::uint32_t>(v);} long long clampTo(long long v, const std::string& type) {    int bits = std::stoi(type.substr(1));    long long lo = type[0] == 'i' ? -(1LL << (bits - 1)) : 0;    long long hi = lo + (1LL << bits) - 1;    return std::clamp(v, lo, hi);} int main() {    std::string line;    std::getline(std::cin, line);    int q = std::stoi(line);    int changed = 0;    for (int i = 0; i < q; i++) {        std::getline(std::cin, line);        std::istringstream in(line);        long long v;        in >> v;        std::string step;        bool first = true;        while (in >> step) {            long long nv;            if (step.back() == '!') {                step.pop_back();                nv = clampTo(v, step);            } else {                nv = wrap(v, step);            }            if (nv != v) changed++;            v = nv;            if (!first) std::cout << ' ';            std::cout << v;   // long long으로 쓴다. int8_t를 그대로 쓰면 글자로 찍힌다            first = false;        }        std::cout << '\n';    }    std::cout << changed << '\n';}
Cmain.c · 51 줄
#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <string.h> /* 잘라 담기: 크기가 정해진 타입으로 캐스트하면 아래쪽 비트만 남는다 */static long long wrap(long long v, const char *type) {    if (strcmp(type, "i8") == 0) return (int8_t)v;    if (strcmp(type, "u8") == 0) return (uint8_t)v;    if (strcmp(type, "i16") == 0) return (int16_t)v;    if (strcmp(type, "u16") == 0) return (uint16_t)v;    if (strcmp(type, "i32") == 0) return (int32_t)v;    return (uint32_t)v;} static long long clamp(long long v, const char *type) {    int bits = atoi(type + 1);    long long lo = type[0] == 'i' ? -(1LL << (bits - 1)) : 0;    long long hi = lo + (1LL << bits) - 1;    return v < lo ? lo : v > hi ? hi : v;} int main(void) {    static char line[256];    int q, changed = 0;    if (!fgets(line, sizeof line, stdin)) return 0;    q = atoi(line);    for (int i = 0; i < q; i++) {        if (!fgets(line, sizeof line, stdin)) break;        char *tok = strtok(line, " \n");        long long v = atoll(tok);        int first = 1;        while ((tok = strtok(NULL, " \n")) != NULL) {            size_t len = strlen(tok);            long long nv;            if (tok[len - 1] == '!') {                tok[len - 1] = '\0';                nv = clamp(v, tok);            } else {                nv = wrap(v, tok);            }            if (nv != v) changed++;            v = nv;            printf(first ? "%lld" : " %lld", v);            first = 0;        }        putchar('\n');    }    printf("%d\n", changed);    return 0;}
Craykonmain.cray · 62 줄
// 2를 bits번 곱한 수둘의제곱(bits){    결과 = 1;    반복 (k = 0; k < bits; k++)    {        결과 = 결과 * 2;    }    돌려주기 결과;} 옮겨담기(v, 단계){    맞추기 = 단계.끝나나("!");    이름 = 단계;    만약 (맞추기)    {        이름 = 단계.부분문자열(0, 단계.길이 - 1);    }    크기 = 둘의제곱(이름.부분문자열(1).수로변환());    lo = 0;    만약 (이름[0] == "i")    {        lo = 0 - 크기 / 2;    }    hi = lo + 크기 - 1;    만약 (맞추기)    {        돌려주기 수학.큰수(lo, 수학.작은수(hi, v));    }    r = v % 크기; // %는 왼쪽 수의 부호를 따라서 음수가 나올 수 있다    만약 (r < 0)    {        r += 크기;    }    만약 (r > hi)    {        r -= 크기;    }    돌려주기 r;} q = 콘솔.줄읽기().수로변환();바뀐수 = 0;반복 (i = 0; i < q; i++){    parts = 콘솔.줄읽기().공백제거().나누기(" ");    v = parts[0].수로변환();    줄 = [];    반복 (j = 1; j < parts.개수; j++)    {        nv = 옮겨담기(v, parts[j]);        만약 (nv != v)        {            바뀐수 += 1;        }        v = nv;        줄.넣기(v);    }    콘솔.쓰기(줄);}콘솔.쓰기(바뀐수);