662 lines
26 KiB
Rust
662 lines
26 KiB
Rust
//! 编译器字面量、VM 算术比较与宿主数值转换共用的表示;构造保存与运算预算分开。
|
||
//! Eq 比较存储表示,numeric_compare 比较数学值;Rust 数值载荷不持有托管对象边。
|
||
//! Lossless number representations. Arithmetic and numeric equivalence are
|
||
//! deliberately separate from representation equality; real operations preserve
|
||
//! exact values unless a binary-float operand explicitly selects float mode.
|
||
|
||
use std::any::Any;
|
||
use std::fmt;
|
||
|
||
use crate::{CompareOp, ObjectPayload, ObjectRef, VmError};
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub struct EndorInteger {
|
||
/// 规范化整数符号,零为 false。
|
||
negative: bool,
|
||
/// 无符号、无前导零的数字串;零保留单个 0。
|
||
digits: String,
|
||
}
|
||
|
||
impl EndorInteger {
|
||
/// Decimal digits of any length, limited only by available memory.
|
||
/// 编译器/宿主解析字面量,构造可保留长数字,实际算术再执行预算检查。
|
||
pub fn parse(text: &str) -> Result<Self, VmError> {
|
||
// 拆符号后校验数字,空串或只有符号均非法。
|
||
let (negative, digits) = split_sign(text);
|
||
// 只允许非空 ASCII 数字,不接受空白、下划线或进制前缀。
|
||
if !is_digits(digits) {
|
||
return Err(VmError::InvalidArgument("invalid integer"));
|
||
}
|
||
// 去前导零,全零统一保存单个正零。
|
||
let digits = digits.trim_start_matches('0');
|
||
Ok(Self {
|
||
negative: negative && !digits.is_empty(),
|
||
digits: if digits.is_empty() { "0" } else { digits }.into(),
|
||
})
|
||
}
|
||
|
||
/// 宿主/转换借用规范化整数绝对值数字串。
|
||
pub fn digits(&self) -> &str {
|
||
&self.digits
|
||
}
|
||
|
||
/// 查询整数符号,构造已消除负零。
|
||
pub fn is_negative(&self) -> bool {
|
||
self.negative
|
||
}
|
||
|
||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||
pub fn is_zero(&self) -> bool {
|
||
self.digits == "0"
|
||
}
|
||
|
||
/// 算术快路径尝试收窄整数,超范围返回 None,不截断。
|
||
pub fn to_i64(&self) -> Option<i64> {
|
||
// 先读 u64 幅值以容纳 i64::MIN 的绝对值,再检查有符号范围。
|
||
let magnitude = self.digits.parse::<u64>().ok()?;
|
||
if self.negative {
|
||
// 最小负数的幅值比最大正数多一,必须单独处理。
|
||
if magnitude == (i64::MAX as u64) + 1 {
|
||
Some(i64::MIN)
|
||
} else {
|
||
i64::try_from(magnitude).ok()?.checked_neg()
|
||
}
|
||
} else {
|
||
i64::try_from(magnitude).ok()
|
||
}
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for EndorInteger {
|
||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
if self.negative {
|
||
f.write_str("-")?;
|
||
}
|
||
f.write_str(&self.digits)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub struct EndorDecimal {
|
||
/// 已验证但未规范化的原十进制字面量。
|
||
literal: String,
|
||
/// 缓存尾数是否全零。
|
||
zero: bool,
|
||
}
|
||
|
||
impl EndorDecimal {
|
||
/// Retains all digits, trailing zeroes, sign, and an arbitrarily long
|
||
/// decimal exponent. No intermediate binary floating-point conversion.
|
||
/// 编译器/宿主解析字面量,构造可保留长数字,实际算术再执行预算检查。
|
||
pub fn parse(text: &str) -> Result<Self, VmError> {
|
||
// 校验时去符号,但最终仍保存完整 text,不丢失显式正号和负零。
|
||
let (_, unsigned) = split_sign(text);
|
||
// 指数最多一段,可带符号;解析不立刻收窄指数到机器整数。
|
||
let mut parts = unsigned.split(['e', 'E']);
|
||
// 尾数后续允许一个点和至少一个数字。
|
||
let mantissa = parts.next().unwrap_or_default();
|
||
// 指数必须有数字且不得有第二个指数段。
|
||
if let Some(exponent) = parts.next()
|
||
&& (!is_digits(split_sign(exponent).1) || parts.next().is_some())
|
||
{
|
||
return Err(VmError::InvalidArgument("invalid decimal exponent"));
|
||
}
|
||
// 分别记录已有小数点、数字数量与尾数是否全零。
|
||
let mut decimal_point = false;
|
||
let mut digits = 0_usize;
|
||
let mut zero = true;
|
||
// 按 ASCII 字节扫描,第二个点或其他字符都拒绝。
|
||
for byte in mantissa.bytes() {
|
||
match byte {
|
||
b'.' if !decimal_point => decimal_point = true,
|
||
b'0'..=b'9' => {
|
||
digits += 1;
|
||
zero &= byte == b'0';
|
||
}
|
||
_ => return Err(VmError::InvalidArgument("invalid decimal")),
|
||
}
|
||
}
|
||
// 允许 .5 和 5.,但不允许只有点或空尾数。
|
||
if digits == 0 {
|
||
return Err(VmError::InvalidArgument("decimal needs digits"));
|
||
}
|
||
Ok(Self {
|
||
literal: text.into(),
|
||
zero,
|
||
})
|
||
}
|
||
|
||
/// 显示和精确转换借用原字面量,保留符号、指数及尾零。
|
||
pub fn literal(&self) -> &str {
|
||
&self.literal
|
||
}
|
||
|
||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||
pub fn is_zero(&self) -> bool {
|
||
self.zero
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for EndorDecimal {
|
||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
f.write_str(&self.literal)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub struct EndorRational {
|
||
/// 带符号分子,不要求已约分。
|
||
numerator: EndorInteger,
|
||
/// 构造保证正且非零的分母。
|
||
denominator: EndorInteger,
|
||
}
|
||
|
||
impl EndorRational {
|
||
/// Normalizes denominator sign, without reducing or rounding the fraction.
|
||
/// 宿主/Store 构造当前数值表示或载荷,具体不变量见下方构造步骤。
|
||
pub fn new(
|
||
mut numerator: EndorInteger,
|
||
mut denominator: EndorInteger,
|
||
) -> Result<Self, VmError> {
|
||
// 构造时拒绝零分母,后续零判断只需看分子。
|
||
if denominator.is_zero() {
|
||
return Err(VmError::InvalidArgument("zero rational denominator"));
|
||
}
|
||
// 分母归正,同时翻分子符号;不执行约分。
|
||
if denominator.negative {
|
||
denominator.negative = false;
|
||
// 零分子保持正零,不引入负零。
|
||
if !numerator.is_zero() {
|
||
numerator.negative = !numerator.negative;
|
||
}
|
||
}
|
||
Ok(Self {
|
||
numerator,
|
||
denominator,
|
||
})
|
||
}
|
||
|
||
/// 显示与精确转换借用分子表示。
|
||
pub fn numerator(&self) -> &EndorInteger {
|
||
&self.numerator
|
||
}
|
||
|
||
/// 显示与精确转换借用正且非零的分母表示。
|
||
pub fn denominator(&self) -> &EndorInteger {
|
||
&self.denominator
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for EndorRational {
|
||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
write!(f, "{}/{}", self.numerator, self.denominator)
|
||
}
|
||
}
|
||
|
||
/// Internal storage forms within one Endor number API, not language classes.
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub enum EndorReal {
|
||
Integer(EndorInteger),
|
||
Decimal(EndorDecimal),
|
||
Rational(EndorRational),
|
||
/// IEEE-754 bits preserve signed zero, infinities and NaN payloads.
|
||
BinaryFloat(u64),
|
||
}
|
||
|
||
impl EndorReal {
|
||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||
pub fn is_zero(&self) -> bool {
|
||
match self {
|
||
Self::Integer(value) => value.is_zero(),
|
||
Self::Decimal(value) => value.is_zero(),
|
||
Self::Rational(value) => value.numerator.is_zero(),
|
||
Self::BinaryFloat(bits) => f64::from_bits(*bits) == 0.0,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for EndorReal {
|
||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
match self {
|
||
Self::Integer(value) => value.fmt(f),
|
||
Self::Decimal(value) => value.fmt(f),
|
||
Self::Rational(value) => value.fmt(f),
|
||
Self::BinaryFloat(bits) => f64::from_bits(*bits).fmt(f),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Unified, lossless number value. PartialEq/Eq compare representations, NOT
|
||
/// mathematical values: decimal scale and IEEE NaN bits remain observable.
|
||
/// Language-level comparison and hashing are not defined by these Rust traits.
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub enum EndorNumber {
|
||
Real(EndorReal),
|
||
Complex {
|
||
/// 复数实部分量。
|
||
real: EndorReal,
|
||
/// 复数虚部分量。
|
||
imaginary: EndorReal,
|
||
},
|
||
}
|
||
|
||
impl EndorNumber {
|
||
/// 编译器/宿主构造整数实数表示,解析错误原样传播。
|
||
pub fn integer(text: &str) -> Result<Self, VmError> {
|
||
Ok(Self::Real(EndorReal::Integer(EndorInteger::parse(text)?)))
|
||
}
|
||
|
||
/// 编译器/宿主构造保留字面量的十进制实数。
|
||
pub fn decimal(text: &str) -> Result<Self, VmError> {
|
||
Ok(Self::Real(EndorReal::Decimal(EndorDecimal::parse(text)?)))
|
||
}
|
||
|
||
/// 宿主/运算结果从两段整数文本建立有理数,拒绝零分母。
|
||
pub fn rational(numerator: &str, denominator: &str) -> Result<Self, VmError> {
|
||
Ok(Self::Real(EndorReal::Rational(EndorRational::new(
|
||
EndorInteger::parse(numerator)?,
|
||
EndorInteger::parse(denominator)?,
|
||
)?)))
|
||
}
|
||
|
||
/// 宿主显式选择二进制浮点,保存全部 IEEE 位。
|
||
pub fn binary_float(value: f64) -> Self {
|
||
Self::Real(EndorReal::BinaryFloat(value.to_bits()))
|
||
}
|
||
|
||
/// 组合两分量,不因虚部为零自动退化为实数;当前不支持复数算术。
|
||
pub fn complex(real: EndorReal, imaginary: EndorReal) -> Self {
|
||
Self::Complex { real, imaginary }
|
||
}
|
||
|
||
/// VM 算术/转换查询实数,Complex 即使虚部为零也返回 None。
|
||
pub fn as_real(&self) -> Option<&EndorReal> {
|
||
match self {
|
||
Self::Real(value) => Some(value),
|
||
Self::Complex { .. } => None,
|
||
}
|
||
}
|
||
|
||
/// VM 快路径仅接受整数变体,不自动转小数或可整除分数。
|
||
pub fn as_i64(&self) -> Option<i64> {
|
||
match self {
|
||
Self::Real(EndorReal::Integer(value)) => value.to_i64(),
|
||
_ => None,
|
||
}
|
||
}
|
||
|
||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||
pub fn is_zero(&self) -> bool {
|
||
match self {
|
||
Self::Real(value) => value.is_zero(),
|
||
Self::Complex { real, imaginary } => real.is_zero() && imaginary.is_zero(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<i64> for EndorNumber {
|
||
/// Rust 宿主转换入口,按输入类型选择整数文本或显式二进制浮点表示。
|
||
fn from(value: i64) -> Self {
|
||
Self::integer(&value.to_string()).expect("i64 always has valid decimal digits")
|
||
}
|
||
}
|
||
|
||
impl From<f64> for EndorNumber {
|
||
/// Rust 宿主转换入口,按输入类型选择整数文本或显式二进制浮点表示。
|
||
fn from(value: f64) -> Self {
|
||
Self::binary_float(value)
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for EndorNumber {
|
||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
match self {
|
||
Self::Real(value) => value.fmt(f),
|
||
Self::Complex { real, imaginary } => write!(f, "({real} + ({imaginary})i)"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct EndorNumberObject {
|
||
/// 拥有数值表示,不含托管对象强边。
|
||
value: EndorNumber,
|
||
}
|
||
|
||
impl EndorNumberObject {
|
||
/// 宿主/Store 构造当前数值表示或载荷,具体不变量见下方构造步骤。
|
||
pub fn new(value: EndorNumber) -> Self {
|
||
Self { value }
|
||
}
|
||
|
||
/// VM/宿主借用堆数值,不增加根或复制内容。
|
||
pub fn value(&self) -> &EndorNumber {
|
||
&self.value
|
||
}
|
||
}
|
||
|
||
impl ObjectPayload for EndorNumberObject {
|
||
/// 数值载荷不可变,不实现旧式原地赋值,允许只读局部重绑定。
|
||
fn reference_assignment_only(&self) -> bool {
|
||
true
|
||
}
|
||
/// Store 的 Rust 载荷下转型入口。
|
||
fn as_any(&self) -> &dyn Any {
|
||
self
|
||
}
|
||
|
||
/// Store 错误与诊断使用的载荷类别,不是名义类型。
|
||
fn kind_name(&self) -> &'static str {
|
||
"number"
|
||
}
|
||
|
||
/// Store 显示数值,不打印对象地址。
|
||
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
|
||
Ok(self.value.to_string())
|
||
}
|
||
|
||
/// Store 真值协议按非零判断,复数要求两分量都为零才为假。
|
||
fn is_true(&self) -> Result<bool, VmError> {
|
||
Ok(!self.value.is_zero())
|
||
}
|
||
|
||
/// 统一数值哈希尚未定义,拒绝以免数学相等而表示不同的键哈希不兼容。
|
||
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
|
||
Err(VmError::UnsupportedOperation("numeric hash"))
|
||
}
|
||
|
||
/// Store 对象比较协议,只在对方也是数值载荷时处理,否则返回 None。
|
||
fn compare(&self, other: &dyn ObjectPayload, op: CompareOp) -> Result<Option<bool>, VmError> {
|
||
// 异类载荷不强行比较,交给 Store 的反向协议或身份回退。
|
||
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
||
// 复数目前连相等比较也不支持,明确报协议错误。
|
||
if self.value.as_real().is_none() || other.value.as_real().is_none() {
|
||
return Err(VmError::UnsupportedOperation("numeric comparison"));
|
||
}
|
||
return self.value.numeric_compare(&other.value, op).map(Some);
|
||
}
|
||
Ok(None)
|
||
}
|
||
}
|
||
|
||
/// 解析共用单个符号拆分,不去空白或重复符号。
|
||
fn split_sign(text: &str) -> (bool, &str) {
|
||
// 只剥离一个前导符号,剩余文本交给数字语法校验。
|
||
if let Some(rest) = text.strip_prefix('-') {
|
||
(true, rest)
|
||
} else {
|
||
(false, text.strip_prefix('+').unwrap_or(text))
|
||
}
|
||
}
|
||
|
||
/// 解析共用非空 ASCII 数字检查,不接受 Unicode 数字。
|
||
fn is_digits(text: &str) -> bool {
|
||
!text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit())
|
||
}
|
||
|
||
/// Source arithmetic is exact for integers/decimals/rationals. A binary-float
|
||
/// operand explicitly selects IEEE floating-point arithmetic, as used by host
|
||
/// geometry/math APIs. Complex arithmetic remains unsupported.
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub enum ArithmeticOp {
|
||
Add,
|
||
Subtract,
|
||
Multiply,
|
||
Divide,
|
||
}
|
||
impl ArithmeticOp {
|
||
/// eval 解 BinaryArithmetic 的 0..3 运算编号,其他值返回 None。
|
||
pub fn from_argument(value: u32) -> Option<Self> {
|
||
match value {
|
||
0 => Some(Self::Add),
|
||
1 => Some(Self::Subtract),
|
||
2 => Some(Self::Multiply),
|
||
3 => Some(Self::Divide),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
impl EndorNumber {
|
||
/// eval 一元负号:浮点翻符号位,其余走零减当前值,因此仍受运算预算限制。
|
||
pub fn negated(&self) -> Result<Self, VmError> {
|
||
// 翻转符号位保留负零和 NaN payload,不经算术规范化。
|
||
if let Some(EndorReal::BinaryFloat(bits)) = self.as_real() {
|
||
return Ok(Self::Real(EndorReal::BinaryFloat(bits ^ (1_u64 << 63))));
|
||
}
|
||
Self::from(0_i64).arithmetic(self, ArithmeticOp::Subtract)
|
||
}
|
||
/// Explicit narrowing for graphics/FFI boundaries; exact values themselves
|
||
/// retain their representation. Non-real or non-finite values are rejected.
|
||
/// 宿主图形/FFI 显式收窄,拒绝复数、非有限值和不可转换范围。
|
||
pub fn to_f64(&self) -> Result<f64, VmError> {
|
||
use num_traits::ToPrimitive;
|
||
// 先要求 Real,不能静默丢弃复数虚部。
|
||
let real = self
|
||
.as_real()
|
||
.ok_or(VmError::UnsupportedOperation("complex to float"))?;
|
||
// 浮点还原原位,精确表示先转 BigRational 再收窄。
|
||
let value = if let EndorReal::BinaryFloat(bits) = real {
|
||
f64::from_bits(*bits)
|
||
} else {
|
||
exact_real(real)?.to_f64().ok_or(VmError::InvalidArgument(
|
||
"number exceeds binary float range",
|
||
))?
|
||
};
|
||
// 图形/FFI 边界不接受 NaN 或无穷。
|
||
if !value.is_finite() {
|
||
return Err(VmError::InvalidArgument("non-finite float conversion"));
|
||
}
|
||
Ok(value)
|
||
}
|
||
/// eval 二元算术:i64 安全快路径,任一浮点选择 float,否则精确有理数运算。
|
||
pub fn arithmetic(&self, other: &Self, op: ArithmeticOp) -> Result<Self, VmError> {
|
||
// 双方为可容纳 i64 的 Integer 才用快路径;运算溢出回退,不截断。
|
||
if let (Some(a), Some(b)) = (self.as_i64(), other.as_i64()) {
|
||
// 加减乘检查溢出,除法仅整除且不溢出时保持整数快路径。
|
||
let integer = match op {
|
||
ArithmeticOp::Add => a.checked_add(b),
|
||
ArithmeticOp::Subtract => a.checked_sub(b),
|
||
ArithmeticOp::Multiply => a.checked_mul(b),
|
||
ArithmeticOp::Divide => a
|
||
.checked_rem(b)
|
||
.filter(|&r| r == 0)
|
||
.and_then(|_| a.checked_div(b)),
|
||
};
|
||
// 快路径成功后包装为规范化整数。
|
||
if let Some(result) = integer {
|
||
return Ok(Self::from(result));
|
||
}
|
||
}
|
||
use num_traits::{ToPrimitive, Zero};
|
||
// 先要求两个输入均为实数;后续按精确/浮点模式转换。
|
||
let (a, b) = (
|
||
self.as_real()
|
||
.ok_or(VmError::UnsupportedOperation("complex arithmetic"))?,
|
||
other
|
||
.as_real()
|
||
.ok_or(VmError::UnsupportedOperation("complex arithmetic"))?,
|
||
);
|
||
// 显式浮点参与才启用 float,精确十进制本身不自动选 float。
|
||
if matches!(a, EndorReal::BinaryFloat(_)) || matches!(b, EndorReal::BinaryFloat(_)) {
|
||
// 混合算术中精确侧可收窄,浮点侧还原原位。
|
||
let convert = |n: &EndorReal| -> Result<f64, VmError> {
|
||
// 已为 float 则还原,否则精确转换后检查能否收窄。
|
||
if let EndorReal::BinaryFloat(bits) = n {
|
||
Ok(f64::from_bits(*bits))
|
||
} else {
|
||
exact_real(n)?.to_f64().ok_or(VmError::InvalidArgument(
|
||
"number exceeds binary float range",
|
||
))
|
||
}
|
||
};
|
||
// 先要求两个输入均为实数;后续按精确/浮点模式转换。
|
||
let (a, b) = (convert(a)?, convert(b)?);
|
||
// 两种运算模式都拒绝零除,包括浮点正负零。
|
||
if matches!(op, ArithmeticOp::Divide) && b == 0. {
|
||
return Err(VmError::InvalidArgument("division by zero"));
|
||
}
|
||
// 执行指定运算;浮点后检查有限性,精确后检查结果预算。
|
||
let result = match op {
|
||
ArithmeticOp::Add => a + b,
|
||
ArithmeticOp::Subtract => a - b,
|
||
ArithmeticOp::Multiply => a * b,
|
||
ArithmeticOp::Divide => a / b,
|
||
};
|
||
// 拒绝 NaN 传播或溢出为无穷,不返回非有限算术结果。
|
||
if !result.is_finite() {
|
||
return Err(VmError::InvalidArgument("non-finite arithmetic result"));
|
||
}
|
||
return Ok(Self::binary_float(result));
|
||
}
|
||
// 先要求两个输入均为实数;后续按精确/浮点模式转换。
|
||
let (a, b) = (exact_real(a)?, exact_real(b)?);
|
||
// 两种运算模式都拒绝零除,包括浮点正负零。
|
||
if matches!(op, ArithmeticOp::Divide) && b.is_zero() {
|
||
return Err(VmError::InvalidArgument("division by zero"));
|
||
}
|
||
// 执行指定运算;浮点后检查有限性,精确后检查结果预算。
|
||
let result = match op {
|
||
ArithmeticOp::Add => a + b,
|
||
ArithmeticOp::Subtract => a - b,
|
||
ArithmeticOp::Multiply => a * b,
|
||
ArithmeticOp::Divide => a / b,
|
||
};
|
||
// 限制运算后分子/分母位数,构造保存上限与实际运算预算不同。
|
||
if result.numer().bits() > 16_384 || result.denom().bits() > 16_384 {
|
||
return Err(VmError::InvalidArgument(
|
||
"arithmetic result exceeds operation budget",
|
||
));
|
||
}
|
||
// 可整除输出 Integer,否则输出约分后 Rational,不伪造 Decimal 小数位。
|
||
if result.is_integer() {
|
||
Self::integer(&result.to_integer().to_string())
|
||
} else {
|
||
Self::rational(&result.numer().to_string(), &result.denom().to_string())
|
||
}
|
||
}
|
||
/// eval/数值载荷数学比较,拒绝复数,处理 NaN 无序与无穷,不依赖 Rust Eq。
|
||
pub fn numeric_compare(&self, other: &Self, op: CompareOp) -> Result<bool, VmError> {
|
||
use std::cmp::Ordering;
|
||
// 先要求两个输入均为实数;后续按精确/浮点模式转换。
|
||
let (a, b) = (
|
||
self.as_real()
|
||
.ok_or(VmError::UnsupportedOperation("complex comparison"))?,
|
||
other
|
||
.as_real()
|
||
.ok_or(VmError::UnsupportedOperation("complex comparison"))?,
|
||
);
|
||
// 双方均为可容纳 i64 的 Integer 时直接比较,不需要大整数分配。
|
||
if let (Some(a), Some(b)) = (self.as_i64(), other.as_i64()) {
|
||
return Ok(match op {
|
||
CompareOp::Eq => a == b,
|
||
CompareOp::Ne => a != b,
|
||
CompareOp::Lt => a < b,
|
||
CompareOp::Le => a <= b,
|
||
CompareOp::Gt => a > b,
|
||
CompareOp::Ge => a >= b,
|
||
});
|
||
}
|
||
// Finite binary floats compare as exact binary rationals, so a large
|
||
// integer never loses precision merely to answer an ordering question.
|
||
// 双浮点使用 partial_cmp;有限混合比较转精确分数,避免大整数失真。
|
||
let order = if let (EndorReal::BinaryFloat(a), EndorReal::BinaryFloat(b)) = (a, b) {
|
||
f64::from_bits(*a).partial_cmp(&f64::from_bits(*b))
|
||
} else {
|
||
// 识别混合比较中的浮点 NaN/无穷,其他表示只有有限值。
|
||
let nonfinite = |n: &EndorReal| match n {
|
||
EndorReal::BinaryFloat(bits) if !f64::from_bits(*bits).is_finite() => {
|
||
Some(f64::from_bits(*bits))
|
||
}
|
||
_ => None,
|
||
};
|
||
// NaN 无序,无穷按符号超出有限值,其他输入走精确比较。
|
||
match (nonfinite(a), nonfinite(b)) {
|
||
(Some(a), _) if a.is_nan() => None,
|
||
(_, Some(b)) if b.is_nan() => None,
|
||
(Some(a), _) => Some(if a.is_sign_positive() {
|
||
Ordering::Greater
|
||
} else {
|
||
Ordering::Less
|
||
}),
|
||
(_, Some(b)) => Some(if b.is_sign_positive() {
|
||
Ordering::Less
|
||
} else {
|
||
Ordering::Greater
|
||
}),
|
||
_ => Some(exact_real(a)?.cmp(&exact_real(b)?)),
|
||
}
|
||
};
|
||
// 无序令 Eq 和有序关系为 false,Ne 为 true。
|
||
Ok(match op {
|
||
CompareOp::Eq => order == Some(Ordering::Equal),
|
||
CompareOp::Ne => order != Some(Ordering::Equal),
|
||
CompareOp::Lt => order == Some(Ordering::Less),
|
||
CompareOp::Le => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
||
CompareOp::Gt => order == Some(Ordering::Greater),
|
||
CompareOp::Ge => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
||
})
|
||
}
|
||
}
|
||
/// 算术/比较/收窄共用精确有理数转换,有限浮点按精确二进制分数处理,有操作预算。
|
||
fn exact_real(value: &EndorReal) -> Result<num_rational::BigRational, VmError> {
|
||
use num_bigint::BigInt;
|
||
use num_rational::BigRational;
|
||
// 大整数转换先限制文本长度,含可能的符号。
|
||
let integer = |s: &str| -> Result<BigInt, VmError> {
|
||
// 超预算报错,不以截断或浮点近似继续。
|
||
if s.len() > 4096 {
|
||
return Err(VmError::InvalidArgument(
|
||
"numeric operand exceeds operation budget",
|
||
));
|
||
}
|
||
s.parse()
|
||
.map_err(|_| VmError::InvalidArgument("invalid exact number"))
|
||
};
|
||
match value {
|
||
EndorReal::Integer(n) => Ok(BigRational::from_integer(integer(&n.to_string())?)),
|
||
EndorReal::Rational(n) => Ok(BigRational::new(
|
||
integer(&n.numerator.to_string())?,
|
||
integer(&n.denominator.to_string())?,
|
||
)),
|
||
EndorReal::BinaryFloat(bits) => BigRational::from_float(f64::from_bits(*bits))
|
||
.ok_or(VmError::InvalidArgument("non-finite exact conversion")),
|
||
EndorReal::Decimal(n) => {
|
||
// 十进制已通过构造语法检查,这里为运算解析指数。
|
||
let parts: Vec<_> = n.literal.split(['e', 'E']).collect();
|
||
// 保存允许很长指数,实际运算要求可转为 i32。
|
||
let exponent = if parts.len() == 2 {
|
||
parts[1].parse::<i32>().map_err(|_| {
|
||
VmError::InvalidArgument("numeric exponent exceeds operation budget")
|
||
})?
|
||
} else {
|
||
0
|
||
};
|
||
// 尾数的小数位数须从指数扣除,确定整体十进制缩放。
|
||
let decimals = parts[0].split_once('.').map_or(0, |(_, s)| s.len()) as i64;
|
||
// 提升到 i64 计算指数差,随后检查绝对幂次预算。
|
||
let power = i64::from(exponent) - decimals;
|
||
// 限制生成巨大十次幂的工作量,不只限制结果显示长度。
|
||
if power.abs() > 4096 {
|
||
return Err(VmError::InvalidArgument(
|
||
"numeric exponent exceeds operation budget",
|
||
));
|
||
}
|
||
// 去掉小数点但保留符号,将尾数转为整数。
|
||
let digits = integer(&parts[0].replace('.', ""))?;
|
||
// 预算通过才计算精确的 10 次幂,不调用浮点 pow。
|
||
let factor = BigInt::from(10).pow(power.unsigned_abs() as u32);
|
||
// 负幂放分母,非负幂乘分子;BigRational 规范化结果。
|
||
Ok(if power < 0 {
|
||
BigRational::new(digits, factor)
|
||
} else {
|
||
BigRational::from_integer(digits * factor)
|
||
})
|
||
}
|
||
}
|
||
}
|