Rust の f64 は安全性のために半順序と半同値までしか与えられない. Float はこの安全性に目をつぶり, 全順序 Ord と全同値 Eq を与える. エイリアスとして次の2つを提供する.
この着想は Rustで競技プログラミング スターターキット #Rust - Qiita から得ました. 作者の
@hatoo さんに感謝します.
/// Def of Float Numbers
use crate::algebra::group_additive::*;
use crate::algebra::monoid::*;
use crate::algebra::ring::*;
use crate::{agroup, monoid, ring}; // IGNORE
#[derive(Debug, Clone, Copy)]
pub struct Float(pub f64);
impl Float {
pub fn abs(&self) -> Self {
Float(self.0.abs())
}
pub fn sin(&self) -> Self {
Float(self.0.sin())
}
pub fn cos(&self) -> Self {
Float(self.0.cos())
}
pub fn tan(&self) -> Self {
Float(self.0.tan())
}
}
agroup! {
Float;
zero = Float(0.0);
add(self, other) = { Float(self.0 + other.0) };
neg(self) = { Float(-self.0) };
}
monoid! {
Float;
one = Float(1.0);
mul(self, other) = { Float(self.0 * other.0) };
}
ring! {
Float;
div(self, other) = { Float(self.0 / other.0) };
}
impl std::ops::Rem for Float {
type Output = Self;
fn rem(self, modulo: Self) -> Float {
Float(self.0 % modulo.0)
}
}
impl std::ops::RemAssign for Float {
fn rem_assign(&mut self, modulo: Self) {
self.0 %= modulo.0;
}
}
impl std::cmp::PartialEq for Float {
fn eq(&self, other: &Self) -> bool {
let allow = 1e-6;
(self.0 - other.0).abs() < allow
|| (self.0 - other.0).abs() / (self.0 + other.0) * 2.0 < allow
}
}
impl std::cmp::Eq for Float {}
impl std::cmp::PartialOrd for Float {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl std::cmp::Ord for Float {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
#[cfg(test)]
mod test_float {
use crate::algebra::group_additive::AGroup;
use crate::num::float::Float;
#[test]
fn test_comparable() {
let mut x = vec![Float(1.0), Float(0.0), Float(-1.0)];
x.sort();
assert_eq!(x, vec![Float(-1.0), Float(0.0), Float(1.0),]);
}
#[test]
fn test_equality() {
let x = Float(3.14);
let y = Float(3.141);
let z = Float(3.140000001);
assert_ne!(x, y);
assert_eq!(x, z);
assert_eq!(Float(0.0), Float::zero());
assert_eq!(Float(2e-7), Float::zero());
}
}