如何将浮点数准确地转换为分数?
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 库:
- Header: number_util.h
- 来源:number_util.c
- 单元测试:number_util_test.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...)
这有望直接移植或包装以用于其他语言。
我正在搜索如何将浮点数转换为最简单的分数,然后找到
问题是,给定的 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 库:
- Header: number_util.h
- 来源:number_util.c
- 单元测试:number_util_test.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...)
这有望直接移植或包装以用于其他语言。