{ }codelper
进制转换取余64 位整数P0403

把循环小数换成另一种进制

把一个用 a 进制写的数转换成 b 进制输出。
这个数不小于 0,小数点后可能无限循环。
循环的部分只写一次并用括号括起来:十进制的 0.1666… 写作 0.1(6),0.142857142857… 写作 0.(142857)。
数位的值为 10 及以上时用英文大写字母:A = 10,B = 11,…,Z = 35。
输入的数是 I、I.F、I.F(R) 三种形式之一。
I 是整数部分,至少一位,除非就是一个字符 0,否则不以 0 开头。
F 是紧跟在小数点后的不循环部分,可以为空;R 是循环部分,至少一位。
有小数点时,F 和 R 不会同时为空。
输入的小数部分不一定是最简写法:也会出现 0.50、0.1(0)、0.12(32)、0.(9) 这样的写法。
输出是同一个值用 b 进制写出的唯一写法。
整数部分不带多余的前导 0(整数部分为 0 时就是一个字符 0)。
值是整数时不写小数点。
小数点后是有限位时不加括号,且最后一位不是 0。
小数点后无限延续时,让循环部分尽可能早地开始,并在其中选最短的一段用括号括起来。
不使用以 (b−1) 无限循环结尾的写法:十进制的 0.(9) 与 1 相等,所以写作 1。
第一行给出用一个空格隔开的 a 和 b,第二行给出用 a 进制写的数,其中没有空格。

输入
2 ≤ a, b ≤ 36。
整数部分 I 的值按十进制小于 10⁹。
设小数点后写出的位数(包括括号内)为 d,则 a^d ≤ 10,000。
每一位是 0~9 或 A~Z,且值小于 a。
输出
一行:用 b 进制写的数(大写,符合上述规则的唯一写法)
样例 1 · stdin
10 2
0.1
stdout
0.0(0011)
样例 2 · stdin
10 6
12.4(9)
stdout
20.3
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

先化成分数,再做竖式除法。
a 进制的 I.F(R) 中 F 有 k 位、R 有 m 位时,值为 p / q,q = a^k × (a^m − 1),p = I × q + F × (a^m − 1) + R(没有 R 时 q = a^k,p = I × q + F)。
把 p 除以 q 的商写成 b 进制;对余数 r,r × b 除以 q 的商就是下一位,余数成为新的 r。
r 变成 0 就结束;之前见过的 r 再次出现时,就从那个 r 产生的那一位开始循环。
r 小于 q,所以 q 步之内必有其一发生。
这样除下去,自然得到最早开始、最短的循环节,也不会出现 (b−1) 无限循环的写法。
每个 r 在何时出现过,Python 用字典记录,其他语言用 q 个格子的数组(q ≤ 10,000;Craykon 用 新列表(q, -1))。
读数位的方式各语言不同:Python int(t, a)、Java Long.parseLong(t, a)、JavaScript/TypeScript parseInt(t, a) 能直接读 2~36 进制,但传入空的 F 时 Python、Java 会抛异常,JavaScript 得到 NaN,所以空字符串要单独当作 0 处理。
C# 的 Convert.ToInt64(t, base) 只接受 2、8、10、16 进制,所以用 Digits.IndexOf(ch);C 用 strchr(DIGITS, c) - DIGITS,C++ 用 DIGITS.find(ch),Craykon 用 DIGITS.查找位置(ch) 求数位的值。
陷阱一:把整数部分和小数部分分开转换,遇到像 0.(9) 这样小数部分恰好为 1 的数就会出错。
9 / 9 的第一位是 10,十进制下输出 0.A。
要把整个 p 除以 q,1 才会进到整数部分。
陷阱二:p 会接近 10⁹ × 10⁴ = 10¹³,在 Java、C#、C、C++ 的 int 中会溢出——要用 long/long long(Python 的整数没有上限,JavaScript 和 Craykon 在 2^53 以内是精确的,所以没问题)。
陷阱三:r 的位置要在用它产生数位之前记下。
放入数位之后再记,十进制的 0.1(6) 会变成 0.16()。
陷阱四:r 变成 0 就要立刻停止。
再除一次 0 会在末尾多出一个 0,十进制的 0.5 在二进制下变成 0.10。
陷阱五:整数部分为 0 时 while (v > 0) 一次也不执行,0.1 会变成 .1(C、C++、C# 的解答用 do-while 避免了)。
陷阱六:JavaScript/TypeScript 的 v.toString(b) 和 Java 的 Long.toString(v, b) 输出小写,需要 toUpperCase()。
陷阱七:在 C、C++、Java、C# 中用 c - '0' 求数位的值,'A' 会变成 17 而不是 10。

Pythonmain.py · 49 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" a, b = map(int, input().split())s = input().strip() # 정수 부분 / 끝나는 부분 F / 되풀이 부분 R 로 나눈다int_str, _, frac = s.partition(".")fixed, _, rep = frac.partition("(")rep = rep.rstrip(")") # 값을 분수 p / q 로 만든다: q = a^k × (a^m − 1)  (R가 없으면 a^k)k, m = len(fixed), len(rep)f_val = int(fixed, a) if fixed else 0if m == 0:    q = a ** k    num = f_valelse:    r_val = int(rep, a)    q = a ** k * (a ** m - 1)    num = f_val * (a ** m - 1) + r_valp = int(int_str, a) * q + num  # 소수 부분이 1이 될 수도 있으니(0.(9)) 한꺼번에 나눈다 whole, r = divmod(p, q) # 정수 부분을 b진법으로head = []while True:    head.append(DIGITS[whole % b])    whole //= b    if whole == 0:        breakout = "".join(reversed(head)) # 나머지를 b배 하며 긴 나눗셈. 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = {}  # 나머지 → 그 나머지로 만든 자리의 위치digits = []while r != 0 and r not in seen:    seen[r] = len(digits)    r *= b    digits.append(DIGITS[r // q])    r %= q if digits:    if r == 0:        out += "." + "".join(digits)    else:        start = seen[r]        out += "." + "".join(digits[:start]) + "(" + "".join(digits[start:]) + ")"print(out)
JavaScriptmain.js · 43 行
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = require("fs").readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b] = line1.split(" ").map(Number);const s = line2.trim(); // a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t) => (t === "" ? 0 : parseInt(t, a)); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/);const intStr = m[1], fixed = m[2] ?? "", rep = m[3] ?? ""; // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q = a ** fixed.length;let num = read(fixed);if (rep !== "") {  const am1 = a ** rep.length - 1;  q *= am1;  num = num * am1 + read(rep);}const p = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 let out = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r = p % q; // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen = new Array(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits = [];while (r !== 0 && seen[r] === -1) {  seen[r] = digits.length;  r *= b;  digits.push(DIGITS[Math.floor(r / q)]);  r %= q;} if (digits.length > 0) {  if (r === 0) out += "." + digits.join("");  else {    const start = seen[r];    out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")";  }}console.log(out);
TypeScriptmain.ts · 46 行
import { readFileSync } from "node:fs";const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b]: number[] = line1.split(" ").map(Number);const s: string = line2.trim(); // a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t: string): number => (t === "" ? 0 : parseInt(t, a)); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/)!;const intStr: string = m[1];const fixed: string = m[2] ?? "";const rep: string = m[3] ?? ""; // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q: number = a ** fixed.length;let num: number = read(fixed);if (rep !== "") {  const am1 = a ** rep.length - 1;  q *= am1;  num = num * am1 + read(rep);}const p: number = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 let out: string = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r: number = p % q; // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen: number[] = new Array<number>(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits: string[] = [];while (r !== 0 && seen[r] === -1) {  seen[r] = digits.length;  r *= b;  digits.push(DIGITS[Math.floor(r / q)]);  r %= q;} if (digits.length > 0) {  if (r === 0) out += "." + digits.join("");  else {    const start = seen[r];    out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")";  }}console.log(out);
JavaMain.java · 71 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Arrays; public class Main {    static final String DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";     // a진법 글자들을 값으로(빈 문자열이면 0)    static long read(String t, int a) {        return t.isEmpty() ? 0 : Long.parseLong(t, a);    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] ab = in.readLine().trim().split(" ");        int a = Integer.parseInt(ab[0]), b = Integer.parseInt(ab[1]);        String s = in.readLine().trim();         // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다        String intStr = s, fixed = "", rep = "";        int dot = s.indexOf('.');        if (dot >= 0) {            intStr = s.substring(0, dot);            String frac = s.substring(dot + 1);            int open = frac.indexOf('(');            if (open >= 0) {                fixed = frac.substring(0, open);                rep = frac.substring(open + 1, frac.length() - 1);            } else {                fixed = frac;            }        }         // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k        long q = 1;        for (int i = 0; i < fixed.length(); i++) q *= a;        long num = read(fixed, a);        if (!rep.isEmpty()) {            long am1 = 1;            for (int i = 0; i < rep.length(); i++) am1 *= a;            am1 -= 1;            q *= am1;            num = num * am1 + read(rep, a);        }        long p = read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다         StringBuilder out = new StringBuilder(Long.toString(p / q, b).toUpperCase()); // toString은 소문자를 낸다        int r = (int) (p % q); // q < 10000이라 나머지는 int에 들어간다         // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다        int[] seen = new int[(int) q]; // 나머지 → 그 나머지로 만든 자리의 위치        Arrays.fill(seen, -1);        StringBuilder digits = new StringBuilder();        while (r != 0 && seen[r] == -1) {            seen[r] = digits.length();            r *= b;            digits.append(DIGITS.charAt((int) (r / q)));            r %= q;        }         if (digits.length() > 0) {            out.append('.');            if (r == 0) out.append(digits);            else {                int start = seen[r];                out.append(digits, 0, start).append('(').append(digits, start, digits.length()).append(')');            }        }        System.out.println(out);    }}
C#Main.cs · 86 行
using System;using System.Text; class Program{    const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";     // a진법 글자들을 값으로(빈 문자열이면 0). Convert.ToInt64(s, base)는 2·8·10·16진법만 받는다    static long Read(string t, int a)    {        long v = 0;        foreach (char ch in t) v = v * a + Digits.IndexOf(ch);        return v;    }     static void Main()    {        string[] ab = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int a = int.Parse(ab[0]), b = int.Parse(ab[1]);        string s = Console.ReadLine().Trim();         // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다        string intStr = s, fixedPart = "", rep = "";        int dot = s.IndexOf('.');        if (dot >= 0)        {            intStr = s[..dot];            string frac = s[(dot + 1)..];            int open = frac.IndexOf('(');            if (open >= 0)            {                fixedPart = frac[..open];                rep = frac[(open + 1)..^1];            }            else fixedPart = frac;        }         // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k        long q = 1;        for (int i = 0; i < fixedPart.Length; i++) q *= a;        long num = Read(fixedPart, a);        if (rep.Length > 0)        {            long am1 = 1;            for (int i = 0; i < rep.Length; i++) am1 *= a;            am1 -= 1;            q *= am1;            num = num * am1 + Read(rep, a);        }        long p = Read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다         long whole = p / q;        var head = new StringBuilder();        do        {            head.Insert(0, Digits[(int)(whole % b)]); // 낮은 자리부터 나오니 앞에 끼운다            whole /= b;        } while (whole > 0);         long r = p % q;        // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다        int[] seen = new int[q]; // 나머지 → 그 나머지로 만든 자리의 위치        Array.Fill(seen, -1);        var digits = new StringBuilder();        while (r != 0 && seen[r] == -1)        {            seen[r] = digits.Length;            r *= b;            digits.Append(Digits[(int)(r / q)]);            r %= q;        }         var outSb = new StringBuilder(head.ToString());        if (digits.Length > 0)        {            string d = digits.ToString();            if (r == 0) outSb.Append('.').Append(d);            else            {                int start = seen[r];                outSb.Append('.').Append(d[..start]).Append('(').Append(d[start..]).Append(')');            }        }        Console.WriteLine(outSb);    }}
C++main.cpp · 79 行
#include <iostream>#include <string>#include <vector>#include <algorithm> const std::string DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 값으로(빈 문자열이면 0)long long readPart(const std::string &t, int a) {    long long v = 0;    for (char ch : t) v = v * a + (long long)DIGITS.find(ch);    return v;} long long power(int a, std::size_t k) {    long long v = 1;    for (std::size_t i = 0; i < k; i++) v *= a;    return v;} int main() {    int a, b;    std::string s;    std::cin >> a >> b >> s;     // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다    std::string intStr = s, fixed, rep;    auto dot = s.find('.');    if (dot != std::string::npos) {        intStr = s.substr(0, dot);        std::string frac = s.substr(dot + 1);        auto open = frac.find('(');        if (open != std::string::npos) {            fixed = frac.substr(0, open);            rep = frac.substr(open + 1, frac.size() - open - 2);        } else {            fixed = frac;        }    }     // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k    long long q = power(a, fixed.size());    long long num = readPart(fixed, a);    if (!rep.empty()) {        long long am1 = power(a, rep.size()) - 1;        q *= am1;        num = num * am1 + readPart(rep, a);    }    long long p = readPart(intStr, a) * q + num; // 10^13까지 커지니 long long     long long whole = p / q; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다    std::string out;    do {        out += DIGITS[whole % b];        whole /= b;    } while (whole > 0);    std::reverse(out.begin(), out.end());     // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다    std::vector<int> seen(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치    std::string digits;    long long r = p % q;    while (r != 0 && seen[r] == -1) {        seen[r] = (int)digits.size();        r *= b;        digits += DIGITS[r / q];        r %= q;    }     if (!digits.empty()) {        out += '.';        if (r == 0) out += digits;        else {            int start = seen[r];            out += digits.substr(0, start) + "(" + digits.substr(start) + ")";        }    }    std::cout << out << '\n';}
Cmain.c · 76 行
#include <stdio.h>#include <string.h> static const char *DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* s[from..to) 의 a진법 글자들을 값으로(비어 있으면 0) */static long long read_part(const char *s, int from, int to, int a) {    long long v = 0;    for (int i = from; i < to; i++)        v = v * a + (long long)(strchr(DIGITS, s[i]) - DIGITS); /* 'A'는 10: c - '0'이 아니다 */    return v;} static long long power(int a, int k) {    long long v = 1;    while (k-- > 0) v *= a;    return v;} int main(void) {    int a, b;    char s[128];    if (scanf("%d %d %127s", &a, &b, s) != 3) return 0;    int len = (int)strlen(s);     /* 정수 부분 [0, dot) / 끝나는 부분 [dot+1, open) / 되풀이 부분 [open+1, len-1) */    char *pdot = strchr(s, '.');    int dot = pdot ? (int)(pdot - s) : len;    char *popen = strchr(s, '(');    int open = popen ? (int)(popen - s) : len;    int fixed_from = dot < len ? dot + 1 : len;     /* 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k */    long long q = power(a, open - fixed_from);    long long num = read_part(s, fixed_from, open, a);    if (open < len) {        long long am1 = power(a, len - 1 - (open + 1)) - 1;        q *= am1;        num = num * am1 + read_part(s, open + 1, len - 1, a);    }    long long p = read_part(s, 0, dot, a) * q + num; /* 10^13까지 커지니 long long */     long long whole = p / q; /* 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 */    char head[64];    int hl = 0;    do {        head[hl++] = DIGITS[whole % b];        whole /= b;    } while (whole > 0);    for (int i = hl - 1; i >= 0; i--) putchar(head[i]);     /* 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 */    static int seen[10000];  /* 나머지 → 그 나머지로 만든 자리의 위치 */    static char digits[10001];    for (int i = 0; i < q; i++) seen[i] = -1;    int n = 0;    long long r = p % q;    while (r != 0 && seen[r] == -1) {        seen[r] = n;        r *= b;        digits[n++] = DIGITS[r / q];        r %= q;    }    digits[n] = '\0';     if (n > 0) {        putchar('.');        if (r == 0) fputs(digits, stdout);        else {            int start = seen[r];            printf("%.*s(%s)", start, digits, digits + start);        }    }    putchar('\n');    return 0;}
Craykonmain.cray · 99 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 앞에서부터 읽어 값으로 만든다(빈 문자열이면 0)읽기(s, a){    v = 0;    遍历 (ch 在 s)    {        v = v * a + DIGITS.查找位置(ch);    }    返回 v;} 거듭(a, k){    v = 1;    循环 (i = 0; i < k; i += 1)    {        v = v * a;    }    返回 v;} ab = 控制台.读数字();a = ab[0];b = ab[1];s = 控制台.读行().去空白(); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다intStr = s;fixed = "";rep = "";dot = s.查找位置(".");如果 (dot >= 0){    intStr = s.截取(0, dot);    frac = s.截取(dot + 1);    open = frac.查找位置("(");    如果 (open >= 0)    {        fixed = frac.截取(0, open);        rep = frac.截取(open + 1, frac.长度 - open - 2);    }    否则    {        fixed = frac;    }} // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^kq = 거듭(a, fixed.长度);num = 읽기(fixed, a);如果 (rep.长度 > 0){    am1 = 거듭(a, rep.长度) - 1;    q = q * am1;    num = num * am1 + 읽기(rep, a);}p = 읽기(intStr, a) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 whole = 数学.商(p, q);r = p % q; // 정수 부분을 b진법으로out = "";当 (真){    out = DIGITS[whole % b] + out;    whole = 数学.商(whole, b);    如果 (whole == 0)    {        跳出;    }} // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = 新列表(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치digits = [];当 (r != 0 && seen[r] == -1){    seen[r] = digits.数量;    r = r * b;    digits.添加(DIGITS[数学.商(r, q)]);    r = r % q;} 如果 (digits.数量 > 0){    如果 (r == 0)    {        out = out + "." + digits.连接("");    }    否则    {        start = seen[r];        out = out + "." + digits.子列表(0, start).连接("") + "(" + digits.子列表(start, digits.数量 - start).连接("") + ")";    }}控制台.输出(out);