use std::io::Write;
use std::cmp;
use float_cmp::ApproxEqUlps;
use {
WriteBuffer,
WriteOptions,
};
pub trait FuzzyEq {
fn fuzzy_eq(&self, other: &f64) -> bool;
#[inline]
fn fuzzy_ne(&self, other: &f64) -> bool {
!self.fuzzy_eq(other)
}
#[inline]
fn is_fuzzy_zero(&self) -> bool {
self.fuzzy_eq(&0.0)
}
}
impl FuzzyEq for f64 {
#[inline]
fn fuzzy_eq(&self, other: &f64) -> bool {
self.approx_eq_ulps(other, 4)
}
}
pub trait FuzzyOrd {
fn fuzzy_cmp(&self, other: &f64) -> cmp::Ordering;
}
impl FuzzyOrd for f64 {
#[inline]
fn fuzzy_cmp(&self, other: &f64) -> cmp::Ordering {
if self.fuzzy_eq(other) {
return cmp::Ordering::Equal;
} else if self > other {
return cmp::Ordering::Greater;
}
cmp::Ordering::Less
}
}
pub fn write_num(num: &f64, rm_leading_zero: bool, buf: &mut Vec<u8>) {
let v = (num * 100_000_000_000.0).round() / 100_000_000_000.0;
let start_pos = buf.len();
write!(buf, "{}", v).unwrap();
if rm_leading_zero {
let mut has_dot = false;
let mut pos = 0;
for c in buf.iter().skip(start_pos) {
if *c == b'.' {
has_dot = true;
break;
}
pos += 1;
}
if has_dot && buf[start_pos + pos - 1] == b'0' {
if pos == 2 && num.is_sign_negative() {
buf.remove(start_pos + 1);
} else if pos == 1 && num.is_sign_positive() {
buf.remove(start_pos);
}
}
}
}
impl WriteBuffer for f64 {
fn write_buf_opt(&self, opt: &WriteOptions, buf: &mut Vec<u8>) {
write_num(self, opt.remove_leading_zero, buf);
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_number {
($name:ident, $num:expr, $rm_zero:expr, $result:expr) => (
#[test]
fn $name() {
let mut v = Vec::new();
write_num(&$num, $rm_zero, &mut v);
assert_eq!(String::from_utf8(v).unwrap(), $result);
}
)
}
test_number!(gen_number_1, 1.0, false, "1");
test_number!(gen_number_2, 0.0, false, "0");
test_number!(gen_number_3, -0.0, false, "0");
test_number!(gen_number_4, -1.0, false, "-1");
test_number!(gen_number_5, 12345678.12345678, false, "12345678.123456782");
test_number!(gen_number_6, -0.1, true, "-.1");
test_number!(gen_number_7, 0.1, true, ".1");
test_number!(gen_number_8, 1.0, true, "1");
test_number!(gen_number_9, -1.0, true, "-1");
test_number!(gen_number_10, 1.5, false, "1.5");
test_number!(gen_number_11, 0.14186, false, "0.14186");
test_number!(gen_number_12, 29.999999999999996, false, "30");
test_number!(gen_number_13, 0.49999999999999994, false, "0.5");
test_number!(gen_number_14, 4273.68, false, "4273.68");
}