
本文介绍如何将形如 10.1(010)₂ 的循环二进制数精确转换为循环十进制数(如 2.6(428571)),全程避免浮点误差,利用有理数运算和 decimal 模块实现任意精度的循环小数表示。
本文介绍如何将形如 `10.1(010)₂` 的循环二进制数精确转换为循环十进制数(如 `2.6(428571)`),全程避免浮点误差,利用有理数运算和 `decimal` 模块实现任意精度的循环小数表示。
要实现无损、精确的循环进制转换,核心在于:不依赖 float,而将输入解析为有理数(分数),再以高精度十进制展开其循环小数形式。
一、将循环二进制数转为有理数(分数)
给定三元组 ('10', '1', '010'),对应二进制数 10.1(010)₂,其数学含义为:
- 整数部分:'10'₂ = 2
- 有限小数部分:'1'₂ × 2⁻¹ = 1/2
- 循环小数部分:'010'₂ = 2,长度为 3,位于小数点后第 1 位起 → 贡献为
[ \frac{2}{2^1 \times (2^3 - 1)} = \frac{2}{2 \times 7} = \frac{1}{7} ]
因此总值为: [ 2 + \frac{1}{2} + \frac{1}{7} = \frac{28 + 7 + 2}{14} = \frac{37}{14} ]
该分数即为精确的有理数表示——这是所有后续高精度转换的基石。
二、用 fractions.Fraction 构建并验证
Python 的 fractions.Fraction 可自动约分并保持精确性:
from fractions import Fraction
def binary_to_rational(integer: str, finite: str, period: str) -> Fraction:
base = 2
# 整数部分
num = int(integer, base) if integer else 0
den = 1
# 有限小数部分:finite × base^(-len(finite))
if finite:
fin_val = Fraction(int(finite, base), base ** len(finite))
num = num * fin_val.denominator + fin_val.numerator * den
den *= fin_val.denominator
# 循环小数部分:period / (base^len(period) - 1) × base^(-offset)
if period:
offset = len(finite)
per_val = Fraction(int(period, base), (base ** len(period)) - 1)
per_val = per_val / (base ** offset)
num = num * per_val.denominator + per_val.numerator * den
den *= per_val.denominator
return Fraction(num, den).limit_denominator()
# 示例:10.1(010)₂ → 37/14
r = binary_to_rational('10', '1', '010')
print(r) # Fraction(37, 14)
三、将有理数精确展开为循环十进制字符串
直接调用 float(r) 会丢失精度。正确做法是模拟长除法,记录余数位置以识别循环节:
def rational_to_repeating_decimal(numerator: int, denominator: int, max_digits=200) -> tuple[str, str, str]:
"""
返回 (integer_part, finite_part, repeating_part) 如 ('2','6','428571')
"""
if denominator == 0:
raise ValueError("Division by zero")
if numerator == 0:
return "0", "", ""
sign = "-" if (numerator <h3>四、高精度验证(可选):用 decimal 模块扩展显示位数</h3><p>若需更多循环节或验证周期长度,可提升 decimal 精度:</p><pre class="brush:php;toolbar:false;">from decimal import Decimal, getcontext
getcontext().prec = 100 # 设置100位精度
result = Decimal(37) / Decimal(14)
print(str(result)) # 自动截断,但可用于交叉验证⚠️ 注意事项:
- 所有中间计算必须使用整数或 Fraction,严禁 float;
- 循环节长度 ≤ 分母的欧拉函数 φ(denominator),实际中通常远小于此;
- 输入三元组需确保 period 非空时 finite 可为空(如 . (101)₂),代码应做健壮性校验;
- 若需反向转换(十进制循环小数 → 二进制),方法对称:先转为分数,再用 rep(n, d, base=2)(见答案中 rep() 函数)。
通过分离「有理数构建」与「小数展开」两个阶段,你既能保证数学严谨性,又能灵活控制输出格式——这才是真正“精确”转换的本质。











