如何将浮点数准确地转换为分数?

How do I exactly convert a float to a fraction?

我正在搜索如何将浮点数转换为最简单的分数,然后找到

问题是,给定的 Python 实现依赖于 python 标准库中的 as_integer_ratio 工具,而 Rust 中不存在该工具。我在评论中询问了这个问题并发现了 f64::frexp 但我不确定我是否理解它是如何工作的,因为它的文档非常神秘(至少对我而言):

Breaks the number into a normalized fraction and a base-2 exponent, satisfying:
self = x * 2^exp
0.5 <= abs(x) < 1.0

此外,这是一个不稳定的功能。

我该怎么办?

除了算法,我想最简单的方法是使用已经可以工作的东西,比如 fraction crate:

来自example

use std::str::FromStr;
use fraction::{Fraction, Sign};  // choose the type accordingly with your needs (see prelude module docs)

fn main() {
    // There are several ways to construct a fraction, depending on your use case

    let f = Fraction::new(1u8, 2u8);  // constructs with numerator/denominator and normalizes the fraction (finds least common denominator)
    assert_eq!(f, Fraction::new_generic(Sign::Plus, 1i32, 2u8).unwrap());  // with numerator/denominator of different integer types
    assert_eq!(f, Fraction::from(0.5));  // convert from float (f32, f64)
    assert_eq!(f, Fraction::from_str("0.5").unwrap());  // parse a string

    // Raw construct with no extra calculations.
    // Most performant, but does not look for common denominator and may lead to unexpected results
    // in following calculations. Only use if you are sure numerator/denominator are already normalized.
    assert_eq!(f, Fraction::new_raw(1u64, 2u64));
}

float-to-fraction 转换的复杂之处在于所有浮点数都已经是带有 power-of-two 分母的有理数,但这可能不是很有用。您正在寻找的是 "best rational approximation",以在某个最大分母内找到最接近目标浮点值的有理数。

该算法(在 link 中进行了描述)背后有一些巧妙的连分数数学运算,但编写代码并不难。这是一个实现它的小型 C 库:

使用示例:

#include "number_util.h"

int numerator;
int denominator;
RationalApproximation(M_PI, 1000000, NULL, &numerator, &denominator);
printf("%d/%d\n", numerator, denominator); 
// Prints: 3126535/995207 (= 3.14159265...)

这有望直接移植或包装以用于其他语言。