1. 引言
在本文中,我们将介绍如何将一个浮点数(float)转换为分数(fraction)。此外,我们还会探讨在需要人类可读的分数表示时,如何通过近似方法或限制分母来实现这一目标。
2. 浮点数与分数
我们的目标是将一个浮点数 $ x $ 转换为一个分数 $ \frac{p}{q} $,其中 $ q > 0 $。例如:
- 若 $ x = 0.3 $,则分数为 $ \frac{3}{10} $
- 若 $ x = 0.33 $,则分数为 $ \frac{33}{100} $
我们会专注于 $ x \geq 0 $ 的情况,因为负数可以通过取绝对值后处理符号来解决。同时,我们假设 $ x \in [0, 1] $,因为大于 1 的情况可以通过取整数部分与小数部分分别处理。
3. 精确分数转换
最直接的方法是将浮点数转换为分母为 10 的幂的分数。例如:
$$ x = 0.33 \quad \mapsto \quad \frac{33}{100} $$
我们通过不断将 $ x $ 乘以 10,直到其小数部分为 0 为止:
algorithm ConvertFloatToPowerOfTenFraction(x):
// INPUT
// x = the float in [0, 1] to convert into a fraction
// OUTPUT
// (p, 10^n) = the fraction form p /10^n of x,
// where the denominator is a power of ten and p >= 0
n <- 0
p <- x
while p - floor(p) > 0:
p <- 10 * p
n <- n + 1
return p / 10^n
3.1 示例
以 $ x = 0.35 $ 为例,算法执行过程如下:
$$ \begin{matrix} p & p - \lfloor p \rfloor & n \ 0.35 & 0.35 & 0 \ 3.5 & 0.5 & 1 \ 35 & 0 & 2 \end{matrix} $$
最终输出 $ \frac{35}{100} $。
3.2 约分处理
上述结果中,分子和分母可能不是互质的。为了简化分数,我们需要用它们的最大公约数(GCD)来约分。例如:
- $ \frac{35}{100} $ 的 GCD 是 5,约分后为 $ \frac{7}{20} $
我们可以使用欧几里得算法(Euclidean Algorithm)来计算 GCD。以下是改进后的算法:
algorithm ConvertFloatToCoprimeFraction(x):
// INPUT
// x = the float to convert into a fraction, where x is in [0, 1]
// OUTPUT
// (p, q) = the fraction form p / q of x with p >= 0 and q > 0,
// where p and q are coprime
n <- 0
p <- x
while p - floor(p) > 0:
p <- 10 * p
n <- n + 1
r <- find the GCD of p and 10^n using the Euclidean Algorithm
p <- p / r
q <- 10^n / r
return (p, q)
4. 近似分数转换
由于浮点数的精度问题,直接转换可能导致不可读的分数,例如:
$$ \frac{330000000001}{1000000000000} $$
为了应对这个问题,我们可以只考虑浮点数的小数点后前几位数字(例如前 5 位),从而得到一个更“友好”的分数。
4.1 截断小数位
我们修改算法,在达到指定小数位数后停止乘以 10:
algorithm ConvertFloatToApproximateFraction(x, m):
// INPUT
// x = the float to convert into a fraction, x in [0, 1]
// m = the number of decimal digits to consider
// OUTPUT
// (p, q) = the fraction form p / q of x,
// where p and q are coprime and |x - p/q| <= 10^(-m)
n <- 0
p <- x
while p - floor(p) > 0 and n ≤ m:
p <- 10 * p
n <- n + 1
r <- find the GCD of p and 10^n using the Euclidean Algorithm
p <- p / r
q <- 10^n / r
return (p, q)
例如,若 $ x = 0.330000000001 $,$ m = 5 $,算法会在 $ n = 5 $ 时停止,此时 $ p = 33000 $,GCD 为 1000,最终结果为 $ \frac{33}{100} $。
4.2 限制分母
有时,即使使用了近似方法,结果分数的分母仍可能难以理解,例如 $ \frac{9429}{12500} $。这时我们可以限制分母为常见的数值(如 10、100、20),并进行后处理。
具体做法是将原始分数 $ \frac{p}{q} $ 转换为以目标分母 $ r $ 表示的形式:
$$ \frac{p}{q} \mapsto \frac{p}{\frac{q}{r} \cdot r} = \frac{\frac{pr}{q}}{r} $$
然后对分子进行四舍五入,得到新的分数。
4.3 示例
假设我们得到了 $ \frac{29}{69} $,想要将其转换为分母为 100 的形式:
$$ \frac{29}{69} \mapsto \frac{29}{\frac{69}{100} \cdot 100} = \frac{\frac{100 \cdot 29}{69}}{100} = \frac{2900}{69 \cdot 100} \approx \frac{42.0289}{100} \approx \frac{42}{100} $$
虽然结果更易读,但代价是精度损失。这种转换方式适用于对可读性要求高于精度的场景。
5. 总结
本文介绍了四种将浮点数转换为分数的方法:
✅ 精确方法:
- 分母为 10 的幂
- 约分后得到最简分数
✅ 近似方法:
- 截断小数位,控制精度
- 限制分母,提高可读性
选择哪种方法取决于你的具体需求:是追求精确性,还是更看重结果的可读性。在实际开发中,通常需要在这两者之间做出权衡。