Комментарии уже указали, почему это происходит. Этот ответ существует, чтобы дать вам способы обойти это.
Первое (и наиболее очевидное) - это использовать библиотеки произвольной точности. Твердый пример этого в ржавчине rug
. Это позволяет вам выразить практически любое число точно, но это вызывает некоторые проблемы за пределами границ FFI (среди других случаев).
Второй - делать то, что большинство людей делает с числами с плавающей запятой, и заключать в скобки ваши равенства. Поскольку вы знаете, что большинство чисел с плавающей точкой не будут сохранены точно, и вы знаете свой тип ввода, вы можете использовать константы, такие как std::f32::MIN
, чтобы заключить в скобки ваш тип, например так ( plays ):
use std::cmp::PartialOrd;
use std::ops::{Add, Div, Sub};
fn bracketed_eq<
I,
E: From<I> + From<f32> + Clone + PartialOrd + Div<Output = E> + Sub<Output = E> + Add<Output = E>,
>(
input: E,
target: I,
value: I,
) -> bool {
let target: E = target.into();
let value: E = value.into();
let bracket_lhs: E = target.clone() - (value.clone() / (2.0).into());
let bracket_rhs: E = target.clone() + (value.clone() / (2.0).into());
bracket_lhs >= input && bracket_rhs <= input
}
#[test]
fn test() {
let u: f32 = 0.23_f32;
assert!(bracketed_eq(f64::from(u), 0.23, std::f32::MIN))
}
Большая часть этого является шаблонной, и многое из этого полностью оптимизируется компилятором;также можно отказаться от требования Clone
, ограничив выбор некоторых черт. Add
, Sub
, Div
предназначены для операций, From<I>
для реализации преобразования, From<f32>
для константы 2.0
.