docs(objects): explain numeric representations and operation budgets
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
//! 编译器字面量、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.
|
||||
@@ -9,17 +11,23 @@ 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(),
|
||||
@@ -27,21 +35,27 @@ impl EndorInteger {
|
||||
})
|
||||
}
|
||||
|
||||
/// 宿主/转换借用规范化整数绝对值数字串。
|
||||
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 {
|
||||
@@ -54,6 +68,7 @@ impl EndorInteger {
|
||||
}
|
||||
|
||||
impl fmt::Display for EndorInteger {
|
||||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.negative {
|
||||
f.write_str("-")?;
|
||||
@@ -64,25 +79,34 @@ impl fmt::Display for EndorInteger {
|
||||
|
||||
#[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,
|
||||
@@ -93,6 +117,7 @@ impl EndorDecimal {
|
||||
_ => return Err(VmError::InvalidArgument("invalid decimal")),
|
||||
}
|
||||
}
|
||||
// 允许 .5 和 5.,但不允许只有点或空尾数。
|
||||
if digits == 0 {
|
||||
return Err(VmError::InvalidArgument("decimal needs digits"));
|
||||
}
|
||||
@@ -102,16 +127,19 @@ impl EndorDecimal {
|
||||
})
|
||||
}
|
||||
|
||||
/// 显示和精确转换借用原字面量,保留符号、指数及尾零。
|
||||
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)
|
||||
}
|
||||
@@ -119,21 +147,27 @@ impl fmt::Display for EndorDecimal {
|
||||
|
||||
#[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;
|
||||
}
|
||||
@@ -144,16 +178,19 @@ impl EndorRational {
|
||||
})
|
||||
}
|
||||
|
||||
/// 显示与精确转换借用分子表示。
|
||||
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)
|
||||
}
|
||||
@@ -170,6 +207,7 @@ pub enum EndorReal {
|
||||
}
|
||||
|
||||
impl EndorReal {
|
||||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||||
pub fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
Self::Integer(value) => value.is_zero(),
|
||||
@@ -181,6 +219,7 @@ impl EndorReal {
|
||||
}
|
||||
|
||||
impl fmt::Display for EndorReal {
|
||||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Integer(value) => value.fmt(f),
|
||||
@@ -198,20 +237,25 @@ impl fmt::Display for EndorReal {
|
||||
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)?,
|
||||
@@ -219,14 +263,17 @@ impl EndorNumber {
|
||||
)?)))
|
||||
}
|
||||
|
||||
/// 宿主显式选择二进制浮点,保存全部 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),
|
||||
@@ -234,6 +281,7 @@ impl EndorNumber {
|
||||
}
|
||||
}
|
||||
|
||||
/// VM 快路径仅接受整数变体,不自动转小数或可整除分数。
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
match self {
|
||||
Self::Real(EndorReal::Integer(value)) => value.to_i64(),
|
||||
@@ -241,6 +289,7 @@ impl EndorNumber {
|
||||
}
|
||||
}
|
||||
|
||||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||||
pub fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
Self::Real(value) => value.is_zero(),
|
||||
@@ -250,18 +299,21 @@ impl EndorNumber {
|
||||
}
|
||||
|
||||
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),
|
||||
@@ -272,45 +324,57 @@ impl fmt::Display for EndorNumber {
|
||||
|
||||
#[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"));
|
||||
}
|
||||
@@ -320,7 +384,9 @@ impl ObjectPayload for EndorNumberObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析共用单个符号拆分,不去空白或重复符号。
|
||||
fn split_sign(text: &str) -> (bool, &str) {
|
||||
// 只剥离一个前导符号,剩余文本交给数字语法校验。
|
||||
if let Some(rest) = text.strip_prefix('-') {
|
||||
(true, rest)
|
||||
} else {
|
||||
@@ -328,6 +394,7 @@ fn split_sign(text: &str) -> (bool, &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析共用非空 ASCII 数字检查,不接受 Unicode 数字。
|
||||
fn is_digits(text: &str) -> bool {
|
||||
!text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit())
|
||||
}
|
||||
@@ -343,6 +410,7 @@ pub enum ArithmeticOp {
|
||||
Divide,
|
||||
}
|
||||
impl ArithmeticOp {
|
||||
/// eval 解 BinaryArithmetic 的 0..3 运算编号,其他值返回 None。
|
||||
pub fn from_argument(value: u32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Add),
|
||||
@@ -354,7 +422,9 @@ impl ArithmeticOp {
|
||||
}
|
||||
}
|
||||
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))));
|
||||
}
|
||||
@@ -362,11 +432,14 @@ impl EndorNumber {
|
||||
}
|
||||
/// 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 {
|
||||
@@ -374,13 +447,17 @@ impl EndorNumber {
|
||||
"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),
|
||||
@@ -390,11 +467,13 @@ impl EndorNumber {
|
||||
.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"))?,
|
||||
@@ -402,8 +481,11 @@ impl EndorNumber {
|
||||
.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 {
|
||||
@@ -412,44 +494,55 @@ impl EndorNumber {
|
||||
))
|
||||
}
|
||||
};
|
||||
// 先要求两个输入均为实数;后续按精确/浮点模式转换。
|
||||
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"))?,
|
||||
@@ -457,6 +550,7 @@ impl EndorNumber {
|
||||
.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,
|
||||
@@ -469,15 +563,18 @@ impl EndorNumber {
|
||||
}
|
||||
// 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,
|
||||
@@ -494,6 +591,7 @@ impl EndorNumber {
|
||||
_ => 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),
|
||||
@@ -504,10 +602,13 @@ impl EndorNumber {
|
||||
})
|
||||
}
|
||||
}
|
||||
/// 算术/比较/收窄共用精确有理数转换,有限浮点按精确二进制分数处理,有操作预算。
|
||||
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",
|
||||
@@ -525,7 +626,9 @@ fn exact_real(value: &EndorReal) -> Result<num_rational::BigRational, VmError> {
|
||||
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")
|
||||
@@ -533,15 +636,21 @@ fn exact_real(value: &EndorReal) -> Result<num_rational::BigRational, VmError> {
|
||||
} 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 {
|
||||
|
||||
Reference in New Issue
Block a user