我正在编写一个函数来修复rust(以及其他语言(中的浮点精度问题。我需要根据传递的参数在函数中选择.floor()
或.ceil()
。解决这个问题的最佳方法是什么。如果某个功能之外的其他功能更好地服务于提议,那么无论如何!谢谢你的帮助!
fn main() {
to_round(7.987, "floor");
}
fn to_round(n: f64, floor_or_ceil: &str) -> f64 {
fn test(diff_n: f64) -> f64 {
if floor_or_ceil == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
}
test(n)
}
如果用闭包替换内部fn
函数,那么您所拥有的代码将被编译。闭包可以引用封闭作用域中的变量,而fn
s不能。
fn main() {
to_round(7.987, "floor");
}
fn to_round(n: f64, floor_or_ceil: &str) -> f64 {
let test = |diff_n: f64| -> f64 {
if floor_or_ceil == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
};
test(n)
}
然而,关闭确实有一些注意事项;特别是,它们不能是泛型函数,也不能在函数指针所在的地方使用(除非它们没有提到("close-over"(封闭范围中的任何变量,并且仅使用闭包语法而不是fn
语法(。
这类问题的最通用的干净解决方案是显式地传递所需的参数,但您可以通过使用结构(尤其是在需要多个值的情况下(和使该结构的函数方法成为来最大限度地减少它的混乱
fn main() {
let c = MathContext { rounding_mode: "floor" };
c.to_round(7.987);
}
struct MathContext {
rounding_mode: &'static str, // Side note: this should really be an enum, not a string
// And you can add more fields here for any other parameters needed.
}
impl MathContext {
fn to_round(&self, n: f64) -> f64 {
self.test(n)
}
fn test(&self, diff_n: f64) -> f64 {
if self.rounding_mode == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
}
}
这套方法是否适用取决于你实际在做什么;如果CCD_ 6对CCD_。但这种模式似乎在代码的其他地方很有用,至少如果你正在做一些事情,比如选择哪种方式来舍入数字。
首先,应该取消使用&str
s作为枚举。只需声明一个枚举:
#[derive(Clone, Copy)]
enum Round {
Floor,
Ceil,
}
use ::Round::{Floor, Ceil};
fn round(n: f64, setting: Round) -> f64 {
match setting {
Floor => n.floor(),
Ceil => n.ceil(),
}
}
第二种变体是使用闭包。
fn round_func(setting: Round) -> impl Fn(f64) -> f64 {
move |n| match setting {
Floor => n.floor(),
Ceil => n.ceil(),
}
}
这将以您的设置作为输入,并返回一个闭合,该闭合将为您计算地板或天花板。EG你可以做一些类似的事情
let closure = round_func(setting);
println!("{}", closure(5.5));
println!("{}", closure(5.5));
这将把配置作为输入,并返回一个用作函数的闭包。
在这种特殊情况下,我们实际上可以通过直接返回函数指针来做得更好。代码如下:
fn round_func_pointer(setting: Round) -> fn(f64) -> f64 {
match setting {
Floor => f64::floor,
Ceil => f64::ceil,
}
}
代码也可以写为
fn round_func_pointer(setting: Round) -> fn(f64) -> f64 {
match setting {
Floor => |n| n.floor(),
Ceil => |n| n.ceil(),
}
}
这是因为";闭包";|n| n.floor()
实际上根本不捕获任何内容(不需要move
关键字(,因此被编译为函数指针。
类变量w/精度和舍入模式
Rust没有像C++、Java和其他语言那样将结构中的字段声明为静态的语法。然而,仍然可以声明一个静态变量,该静态变量对于所有意图和目的来说都是"0";类变量";。
在下面的实现中,静态变量CONFIG
的作用域被限制为它在其中声明的文件;因此,从这个意义上说,它不是一个";全局";变量在同一文件中声明的结构对其具有独占访问权限,并且该结构的所有实例都访问同一变量。
为了确保";类变量";RwLock
用于同步它。与在单线程环境中不同步访问的实现相比,这会带来较小的性能成本。
CCD_ 13宏建立";类变量";以延迟初始化,直到运行时实际需要该变量。
use std::sync::RwLock;
#[macro_use]
extern crate lazy_static;
lazy_static! {
// The tuple holds (rounding-mode, precision).
static ref CONFIG: RwLock<(RoundMode, u32)> = {
RwLock::new((RoundMode::Auto, 3))
};
}
#[derive(Clone, Copy)]
pub enum RoundMode { Ceil, Floor, Auto }
下面的结构实现为客户端代码提供了一种设置HasValue
所有实例的舍入模式(Auto、Ceil和Floor(和精度(要舍入到的小数位数(的方法。
pub struct HasValue { value: f64 }
impl HasValue {
pub fn new(value: f64) -> Self
{
HasValue { value }
}
pub fn config() -> (RoundMode, u32)
{
*CONFIG.read().unwrap()
}
pub fn set_config(mode: RoundMode, precision: u32)
{
*CONFIG.write().unwrap() = (mode, precision);
}
pub fn round(&self) -> f64
{
use RoundMode::*;
let (mode, prec) = HasValue::config();
let base = 10_f64.powf(prec as f64);
match mode {
Auto => (self.value * base).round() / base,
Ceil => (self.value * base).ceil() / base,
Floor => (self.value * base).floor() / base,
}
}
}
正在创建、配置、打印。。。
fn main()
{
let v = HasValue::new(7.98555);
let r = v.round();
println!("r={:?}", r);
HasValue::set_config(RoundMode::Floor, 4);
let r = v.round();
println!("r={:?}", r);
}