#![forbid(unsafe_code)]
use std::str::FromStr;
use oxinum_float::{
precision::{epsilon, ulp, with_precision},
DBig, OxiNumError,
};
#[test]
fn with_precision_rebinds_to_requested_precision() {
let a = DBig::from_str("1.234").expect("parse 1.234");
assert_eq!(a.precision(), 4);
let up = with_precision(&a, 100);
assert_eq!(up.precision(), 100);
assert_eq!(up, a, "up-rebinding must preserve numeric value");
let down = with_precision(&a, 2);
assert_eq!(down.precision(), 2);
}
#[test]
fn with_precision_zero_means_unlimited() {
let a = DBig::from_str("1.234").expect("parse 1.234");
let unlimited = with_precision(&a, 0);
assert_eq!(unlimited.precision(), 0);
}
#[test]
fn epsilon_10_matches_one_e_minus_nine_at_precision_10() {
let eps = epsilon(10).expect("epsilon(10)");
assert_eq!(eps.precision(), 10);
let expected = DBig::from_str("1e-9")
.expect("parse 1e-9")
.with_precision(10)
.value();
assert_eq!(eps, expected, "epsilon(10) should equal 1e-9 @ p10");
}
#[test]
fn epsilon_1_matches_one() {
let eps = epsilon(1).expect("epsilon(1)");
assert_eq!(eps.precision(), 1);
let expected = DBig::from_str("1")
.expect("parse 1")
.with_precision(1)
.value();
assert_eq!(eps, expected);
}
#[test]
fn epsilon_50_matches_one_e_minus_49_at_precision_50() {
let eps = epsilon(50).expect("epsilon(50)");
assert_eq!(eps.precision(), 50);
let expected = DBig::from_str("1e-49")
.expect("parse 1e-49")
.with_precision(50)
.value();
assert_eq!(eps, expected);
}
#[test]
fn epsilon_zero_is_an_error() {
match epsilon(0) {
Err(OxiNumError::Precision(_)) => {}
other => panic!("expected Precision error, got {other:?}"),
}
}
#[test]
fn ulp_of_one_point_two_three_is_one_hundredth() {
let x = DBig::from_str("1.23").expect("parse 1.23");
let u = ulp(&x).expect("ulp at finite precision");
assert_eq!(u, DBig::from_str("0.01").expect("parse 0.01"));
}
#[test]
fn ulp_preserves_carried_precision() {
for s in ["1.23", "0.001", "1234567890", "9.87654321e-12"] {
let x = DBig::from_str(s).unwrap_or_else(|e| panic!("parse {s}: {e}"));
let u = ulp(&x).expect("ulp");
assert_eq!(
u.precision(),
x.precision(),
"ulp({s}) must keep precision {} (got {})",
x.precision(),
u.precision(),
);
}
}
#[test]
fn ulp_at_precision_10_matches_epsilon_10_at_unit_value() {
let one_at_p10 = DBig::from_str("1")
.expect("parse 1")
.with_precision(10)
.value();
assert_eq!(one_at_p10.precision(), 10);
let u = ulp(&one_at_p10).expect("ulp(1 @ p10)");
let eps = epsilon(10).expect("epsilon(10)");
assert_eq!(u, eps, "ulp(1 @ p10) should match epsilon(10)");
}
#[test]
fn ulp_of_unlimited_precision_is_an_error() {
let unlimited = DBig::from_str("1.23")
.expect("parse 1.23")
.with_precision(0)
.value();
assert_eq!(unlimited.precision(), 0);
match ulp(&unlimited) {
Err(OxiNumError::Precision(_)) => {}
other => panic!("expected Precision error, got {other:?}"),
}
}
#[cfg(feature = "serde")]
#[test]
fn dbig_json_roundtrip_via_dashu_serde() {
for s in ["0", "1.23", "-4.5e-10", "3.14159265358979"] {
let x = DBig::from_str(s).unwrap_or_else(|e| panic!("parse {s}: {e}"));
let json = serde_json::to_string(&x).expect("serialize DBig");
let back: DBig = serde_json::from_str(&json).expect("deserialize DBig");
assert_eq!(back, x, "round-trip mismatch for {s} via JSON: {json}");
}
}