// 标准库入口来自实际 Dever 源码;参照只使用 dec,不调用 Dever 运行库。 use dec::{Context, Decimal128}; use std::sync::LazyLock; static NATIVE_CONTEXT: LazyLock> = LazyLock::new(Context::default); struct DecimalInput { text: String, value: Decimal, native: Decimal128, places: i64, } fn native_parse(text: &str) -> Option { let mut context = NATIVE_CONTEXT.clone(); let value = context.parse(text).ok()?; (value.is_finite() && !context.status().inexact()).then_some(value) } fn parsed(values: &[String]) -> Result>, Fault> { values .iter() .map(|text| { Ok(if NATIVE { native_parse(text).map(|value| value.to_be_bytes()) } else { DECIMAL_PARSE(text)?.map(Decimal::to_be_bytes) }) }) .collect() } struct NativeDisplay(Decimal128); impl std::fmt::Display for NativeDisplay { fn fmt(&self, output: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let value = Context::::default().reduce(self.0); if value.is_zero() { output.write_str("0") } else { write!(output, "{value}") } } } fn formatted(values: &[DecimalInput]) -> Result, Fault> { values .iter() .map(|input| { if NATIVE { Ok(NativeDisplay(input.native).to_string()) } else { DECIMAL_TEXT(input.value) } }) .collect() } fn native_round(value: Decimal128, places: i64) -> Option { let exponent = i32::try_from(places.checked_neg()?).ok()?; if !(-6176..=6111).contains(&exponent) { return None; } let mut context = NATIVE_CONTEXT.clone(); let mut quantum = Decimal128::ONE; context.set_exponent(&mut quantum, exponent); let value = context.quantize(value, quantum); (value.is_finite() && !context.status().underflow() && !context.status().invalid_context()) .then_some(value) } fn rounded(values: &[DecimalInput]) -> Result>, Fault> { Ok(values .iter() .map(|input| { if NATIVE { native_round(input.native, input.places).map(|value| value.to_be_bytes()) } else { DECIMAL_ROUND(input.value, input.places) .ok() .map(Decimal::to_be_bytes) } }) .collect()) } fn negated(values: &[DecimalInput]) -> Result, Fault> { Ok(values .iter() .map(|input| { if NATIVE { (-input.native).to_be_bytes() } else { (-input.value).to_be_bytes() } }) .collect()) } fn compared( values: &[DecimalInput], ) -> Result)>, Fault> { Ok(values .windows(2) .map(|pair| { if NATIVE { ( pair[0].native == pair[1].native, pair[0].native.partial_cmp(&pair[1].native), ) } else { ( pair[0].value == pair[1].value, pair[0].value.partial_cmp(&pair[1].value), ) } }) .collect()) } fn byte_score(values: &[Option<[u8; 16]>]) -> u64 { values .iter() .flatten() .flat_map(|value| value.iter()) .map(|value| u64::from(*value)) .sum() } fn text_score(values: &[String]) -> u64 { values .iter() .flat_map(|value| value.bytes()) .map(u64::from) .sum() } fn main() { println!("case,sample,implementation,nanoseconds,checksum"); let spellings = [ "0", "-0", "1.2300", "-12.345", "1E-6176", "1E6111", "9.999999999999999999999999999999999E6144", "1234567890123456789012345678901234", "1.25", "1.35", "-250", "+.125", "1e-34", "1.000000000000000000000000000000000", ]; let texts = spellings .iter() .cycle() .take(112) .map(|text| (*text).to_owned()) .collect::>(); let inputs = texts .iter() .enumerate() .map(|(index, text)| DecimalInput { text: text.clone(), value: Decimal::parse(text).unwrap(), native: native_parse(text).unwrap(), places: [0, 1, 2, -2, 6176, -6111][index % 6], }) .collect::>(); compare( "decimal_parse_valid", &texts, 1000, parsed::, parsed::, |value| byte_score(value), ); let invalid = [ "", "+", "NaN", "-sNaN", "Infinity", "nan123", " 1", "1 ", "1_000", "1e", "1\0", "雪", "1e9999", "1e-9999", "12345678901234567890123456789012345", ] .iter() .cycle() .take(120) .map(|value| (*value).to_owned()) .collect::>(); assert!( parsed::(&invalid) .unwrap() .iter() .all(Option::is_none) ); compare( "decimal_parse_invalid", &invalid, 1000, parsed::, parsed::, |value| byte_score(value), ); compare( "decimal_format", &inputs, 1000, formatted::, formatted::, |value| text_score(value), ); compare( "decimal_round", &inputs, 1000, rounded::, rounded::, |value| byte_score(value), ); compare( "decimal_negate", &inputs, 1000, negated::, negated::, |value| value.iter().flatten().map(|value| u64::from(*value)).sum(), ); compare( "decimal_compare", &inputs, 1000, compared::, compared::, |value| { value .iter() .map(|(equal, order)| { u64::from(*equal) + u64::from(order == &Some(std::cmp::Ordering::Less)) }) .sum() }, ); wire_cases(&inputs); storage_cases(); }