// 算术参照直接调用 dec,逐步检查同样的错误;不降低精度或重排运算。 use dec::{Context, Decimal128}; const TWO: Decimal128 = Decimal128::from_ne_bytes( u128::from_be_bytes([34, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]).to_ne_bytes(), ); // 两套表示在计时前准备,Rust 循环不经过 Dever 的输入/输出转换。 struct Inputs { generated: Vec, native: Vec, } fn inputs( values: &[Generated], convert: impl FnMut(Generated) -> Native, ) -> [Inputs; 1] { [Inputs { generated: values.to_vec(), native: values.iter().copied().map(convert).collect(), }] } fn native_bytes(value: Decimal128) -> [u8; 16] { u128::from_ne_bytes(value.to_ne_bytes()).to_be_bytes() } fn raw(value: Decimal) -> Decimal128 { Decimal128::from_be_bytes(value.to_be_bytes()) } fn checked(value: Decimal128, context: &Context) -> Result { if value.is_finite() && !context.status().overflow() && !context.status().invalid_operation() { Ok(value) } else { Err(&FaultInfo { message: "Decimal arithmetic fault", source: "Rust reference", line: 0, column: 0, }) } } fn operation( context: &mut Context, left: Decimal128, right: Decimal128, ) -> Result { context.clear_status(); let value = match KIND { 0 => context.add(left, right), 1 => context.sub(left, right), 2 => context.mul(left, right), 3 => context.div(left, right), 4 => { let product = operation::<2>(context, left, right)?; let added = operation::<0>(context, product, left)?; let divided = operation::<3>(context, added, right)?; return operation::<1>(context, divided, left); } 5 => context.mul(left, TWO), _ => unreachable!(), }; checked(value, context) } fn generated_one(left: Decimal, right: Decimal) -> Result { match KIND { 0 => GENERATED_ADD(left, right), 1 => GENERATED_SUBTRACT(left, right), 2 => GENERATED_MULTIPLY(left, right), 3 => GENERATED_DIVIDE(left, right), 4 => GENERATED_CALCULATE(left, right), 5 => GENERATED_SCALE(left), _ => unreachable!(), } } fn score(bytes: [u8; 16]) -> u64 { u64::from_be_bytes(bytes[8..].try_into().unwrap()) } fn generated( inputs: &[Inputs<(Decimal, Decimal), (Decimal128, Decimal128)>], ) -> Result { let values = inputs.first().map_or(&[][..], |input| input.generated.as_slice()); let mut total = 0u64; for (left, right) in values { total = total.wrapping_add(score(generated_one::(*left, *right)?.to_be_bytes())); } Ok(total) } fn reference( inputs: &[Inputs<(Decimal, Decimal), (Decimal128, Decimal128)>], ) -> Result { let values = inputs.first().map_or(&[][..], |input| input.native.as_slice()); let mut context = Context::::default(); let mut total = 0u64; for (left, right) in values { let value = operation::(&mut context, *left, *right)?; total = total.wrapping_add(score(native_bytes(value))); } Ok(total) } fn compare_operation(name: &str, values: &[(Decimal, Decimal)]) { let mut context = Context::::default(); let maximum = Decimal::parse("9.999999999999999999999999999999999E+6144").unwrap(); for (left, right) in values .iter() .copied() .chain([(maximum, maximum), (maximum, Decimal::from_int(0))]) { let actual = generated_one::(left, right); let expected = operation::(&mut context, raw(left), raw(right)); assert_eq!(actual.is_err(), expected.is_err()); if let (Ok(actual), Ok(expected)) = (actual, expected) { assert_eq!(actual.to_be_bytes(), expected.to_be_bytes()); } } let inputs = inputs(values, |(left, right)| (raw(left), raw(right))); compare( name, &inputs, 1000, generated::, reference::, |value| *value, ); } fn projected(inputs: &[Inputs]) -> Result<[u8; 16], Fault> { let values = inputs.first().map_or(&[][..], |input| input.generated.as_slice()); GENERATED_SCALED(values).map(Decimal::to_be_bytes) } fn projected_reference(inputs: &[Inputs]) -> Result<[u8; 16], Fault> { let values = inputs.first().map_or(&[][..], |input| input.native.as_slice()); let mut context = Context::::default(); let mut total = Decimal128::ZERO; for value in values { let next = operation::<5>(&mut context, *value, Decimal128::ZERO)?; total = operation::<0>(&mut context, total, next)?; } Ok(native_bytes(total)) } fn converted(values: &[i64]) -> Result { let mut total = 0u64; for value in values { total = total.wrapping_add(score(GENERATED_CONVERT(*value)?.to_be_bytes())); } Ok(total) } fn converted_reference(values: &[i64]) -> Result { let mut total = 0u64; for value in values { let decimal = if let Ok(narrow) = i32::try_from(*value) { Decimal128::from(narrow) } else if let Ok(narrow) = u32::try_from(*value) { Decimal128::from(narrow) } else { Decimal128::from(*value) }; total = total.wrapping_add(score(native_bytes(decimal))); } Ok(total) } fn converted_direct_reference(values: &[i64]) -> Result { let mut total = 0u64; for value in values { total = total.wrapping_add(score(native_bytes(Decimal128::from(*value)))); } Ok(total) } fn main() { let constants = GENERATED_CONSTANTS().unwrap(); let expected = [ Decimal128::ZERO, Context::::default().parse("-0.00").unwrap(), Decimal128::from(i64::MIN), Decimal128::from(i64::MAX), Decimal128::from(3i64), ]; assert_eq!(constants.len(), expected.len()); for (actual, expected) in constants.iter().zip(expected) { assert_eq!(actual.to_be_bytes(), expected.to_be_bytes()); } let left = [ "-0.00", "1.25", "-7.5", "1E-60", "9999999999999999999999999999999999", "-123.456", "0.9999999999999999999999999999999999", "1234567.890", ] .map(|text| Decimal::parse(text).unwrap()); let right = [ "3.25", "-2", "0.1", "1.000000000000000000000000000000001", "2", "10", "-3", "1", ] .map(|text| Decimal::parse(text).unwrap()); let values = (0..256) .map(|index| (left[index % left.len()], right[index % right.len()])) .collect::>(); println!("case,sample,implementation,elapsed_ns,checksum"); compare_operation::<0>("decimal_add", &values); compare_operation::<1>("decimal_subtract", &values); compare_operation::<2>("decimal_multiply", &values); compare_operation::<3>("decimal_divide", &values); compare_operation::<4>("decimal_calculate", &values); compare_operation::<5>("decimal_literal_scale", &values); let items = values.iter().map(|(left, _)| *left).collect::>(); let prepared = inputs(&items, raw); assert_eq!(projected(&prepared).unwrap(), projected_reference(&prepared).unwrap()); let large = Decimal::parse("4E+6144").unwrap(); let maximum = Decimal::parse("9.999999999999999999999999999999999E+6144").unwrap(); for items in [vec![], vec![large, large, maximum], vec![maximum, large]] { let prepared = inputs(&items, raw); let actual = projected(&prepared); let expected = projected_reference(&prepared); assert_eq!(actual.is_err(), expected.is_err()); match (actual, expected) { (Ok(actual), Ok(expected)) => assert_eq!(actual, expected), (Err(actual), Err(_)) => { let source = std::fs::read_to_string(actual.source).unwrap(); let needle = if items[0] == maximum { "value * 2" } else { "sum(scale, values)" }; assert_eq!( actual.line, source[..source.find(needle).unwrap()] .bytes() .filter(|byte| *byte == b'\n') .count() + 1 ); } _ => unreachable!(), } } compare( "decimal_projected_sum", &prepared, 1000, projected, projected_reference, |value| score(*value), ); let numbers = [ i64::MIN, i64::MAX, -1, 0, 1, i32::MIN as i64, i32::MAX as i64, 1234567890123456789, ]; for value in numbers { assert_eq!( GENERATED_CONVERT(value).unwrap().to_be_bytes(), Decimal128::from(value).to_be_bytes() ); } let integers = (0..256) .map(|index| numbers[index % numbers.len()]) .collect::>(); compare( "decimal_int_conversion", &integers, 1000, converted, converted_reference, |value| *value, ); for (name, numbers) in [ ("decimal_int_conversion_narrow", [ i64::from(i32::MIN), -1, 0, 1, i64::from(i32::MAX), i64::from(i32::MAX) + 1, i64::from(u32::MAX) - 1, i64::from(u32::MAX), ]), ("decimal_int_conversion_wide", [ i64::MIN, i64::MAX, i64::from(i32::MIN) - 1, i64::from(u32::MAX) + 1, -1234567890123456789, 1234567890123456789, -4294967296, 4294967296, ]), ] { let values = (0..256).map(|index| numbers[index % numbers.len()]).collect::>(); compare(name, &values, 1000, converted, converted_reference, |value| *value); if name.ends_with("wide") { compare_named("decimal_int_conversion_wide_direct", &values, 1000, converted, converted_direct_reference, |value| *value, "rust_direct"); } } }