narust_158/entity/float_values/
short_float.rs

1//! 🎯复刻OpenNARS `nars.entity.ShortFloat`
2//! * 🚩核心逻辑:一个前提,多个派生,多方聚合
3//!   * 前提:通过实现[`EvidenceReal`]得到「基本操作」
4//!   * 派生:通过实现各类`XXXFunctions`得到「派生操作」
5//!   * 聚合:通过统一的「自动实现」得到「所有操作汇聚于一体」的静态功能增强(真值函数@数值)
6//!     * 📝Rust允许「在外部调用『看似没有实现派生操作的结构』时,允许使用『自动实现了的派生操作』」
7//! * 🕒最后更新:【2024-06-19 23:36:50】
8//!
9//! * ✅【2024-05-02 21:41:48】(初代实现)基本复刻完毕
10//! * ♻️【2024-06-19 23:36:56】删繁就简:删去「抽象特征」以免去后续诸多泛型引入的代码复杂性
11
12use crate::{global::Float, impl_display_from_to_display, util::ToDisplayAndBrief};
13use narsese::api::EvidentNumber;
14use std::ops::{Add, BitAnd, BitOr, Div, Mul, Not, Sub};
15use thiserror::Error;
16
17/// 用作「短浮点」的整数类型
18/// * 🚩使用0~4294967296的「三十二位无符号整数」覆盖`0~10000`与(相乘时的)`0~100000000`
19/// * 🎯在「短浮点乘法」处避免重复的`as`转换(以提升性能⚡)
20///   * 📄【2024-05-02 11:38:12】总测试时间从原先`(3.5+x)s`变为`3.23s`(用空间换时间后)
21type UShort = u32;
22
23/// 用作「短浮点」的范围上界
24/// * 🚩表示区间`0~10000`
25const SHORT_MAX: UShort = 10000;
26
27/// 用作「整数→浮点」的转换倍率
28/// * 🚩【2024-05-02 09:27:03】目前相当于「直接除以一万」
29const MULTIPLIER_TO_FLOAT: Float = 0.0001;
30
31/// 用作「浮点→整数」的转换倍率
32/// * 🚩【2024-05-02 09:27:03】目前相当于「直接乘以一万」
33const MULTIPLIER_TO_UINT: Float = 10000.0;
34
35/// 模拟`nars.entity.ShortFloat`
36/// * 🚩使用`u32`0~4294967296的范围覆盖`0~10000²`
37/// * ✨原生支持四则运算
38/// * 🎯在基本的[「证据数」](EvidentNumber)基础上,添加更多NAL细节功能
39///   * 📄原[`nars.inference.UtilityFunctions`](crate::inference::UtilityFunctions)的「扩展逻辑与或非」
40/// * 🚩【2024-05-02 16:05:04】搬迁自[`crate::entity::BudgetValue`]
41/// * 🚩【2024-05-02 17:48:30】现在全部抛弃基于「不可变引用」的运算
42///   * ⚠️混合「传可变引用」和「直接传值」的代码将过于冗杂(并且造成接口不统一)
43///   * 📌在实现了[`Copy`]之后,将值的复制看作是「随处可用」的
44/// * 🚩【2024-05-03 11:11:48】现在将其概念与「短浮点」合并
45///
46/// ## ⚠️与OpenNARS不同的一点:浮点舍入问题
47///
48/// !📝OpenNARS的实现是「四舍五入」,而NARust的实现是「向下截断」
49/// * ❗即便在构造时采用了[`Float::round`],但实际效果仍然与OpenNARS不同
50///   * ⚡为性能考量,许多运算最后的舍入操作仍然是四舍五入(整数除法,避免转换为浮点)
51/// * 📄这导致`0.1 * 0.0005`在OpenNARS中等于`0.0001`而在NARust中为`0`
52///
53/// OpenNARS中可行的推理:
54///
55/// ```plaintext
56/// IN: <A --> B>. %1.00;0.10% {6 : 3}
57/// IN: <B --> C>. %1.00;0.01% {6 : 4}
58/// 1
59/// OUT: <A --> C>. %1.00;0.00% {7 : 4;3}
60/// OUT: <C --> A>. %1.00;0.00% {7 : 4;3}
61/// ```
62///
63/// # 📄OpenNARS
64///
65/// A float value in [0, 1], with 4 digits accuracy.
66#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67pub struct ShortFloat {
68    /// 0~4294967296的「实际值」
69    ///
70    /// # 📄OpenNARS
71    ///
72    /// To save space, the values are stored as short integers (-32768 to 32767, only
73    /// 0 to 10000 used),
74    /// but used as float
75    value: UShort,
76}
77
78/// 定制的序列反序列化方法
79/// * 🎯节省序列化后的占用空间
80///   * 📄在JSON中不再需要是一个object,是一个number就行了
81mod serde {
82    use super::{ShortFloat, UShort};
83    use serde::{Deserialize, Deserializer, Serialize, Serializer};
84
85    impl Serialize for ShortFloat {
86        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87        where
88            S: Serializer,
89        {
90            // 直接委托到内部整数值
91            self.value.serialize(serializer)
92        }
93    }
94
95    impl<'de> Deserialize<'de> for ShortFloat {
96        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
97        where
98            D: Deserializer<'de>,
99        {
100            // 先反序列化到内部整数值
101            let value = UShort::deserialize(deserializer)?;
102            // 然后尝试创建,并在其中转换Error类型
103            Self::new(value).map_err(serde::de::Error::custom)
104        }
105    }
106}
107
108/// 用于表示「短浮点」可能产生的错误
109#[derive(Debug, Clone, Error)]
110pub enum ShortFloatError {
111    #[error("value out of range: {0}")]
112    OutOfRange(Float),
113}
114/// 符合[`Result`]的「短浮点结果」
115pub type ShortFloatResult = Result<ShortFloat, ShortFloatError>;
116
117impl ShortFloat {
118    /// 常量「0」
119    pub const ZERO: Self = Self::new_unchecked(0);
120
121    /// 常量「1」
122    pub const ONE: Self = Self::new_unchecked(SHORT_MAX);
123
124    /// 常量「1/2」
125    pub const HALF: Self = Self::new_unchecked(SHORT_MAX / 2);
126
127    /// 以0~10000的整数创建(有检查)
128    #[inline(always)]
129    pub fn new(value: UShort) -> Result<Self, ShortFloatError> {
130        Self::new_unchecked(value).validate()
131    }
132
133    /// 以0~10000的整数创建(无检查)
134    /// * ⚠️部分封闭:仅对[`crate::entity`]模块开放
135    pub(super) const fn new_unchecked(value: UShort) -> Self {
136        Self { value }
137    }
138
139    /// 🆕判断浮点数是否在范围内
140    /// * 📝判断「是否在范围外」直接使用「不在范围内」的逻辑
141    ///   * 📄clippy提示「manual `!RangeInclusive::contains` implementation」
142    /// * ✅对`NaN`会默认返回`false`,故无需担心
143    #[inline(always)]
144    pub fn is_in_range(value: Float) -> bool {
145        (0.0..=1.0).contains(&value)
146    }
147
148    /// 模拟`getValue`
149    /// * 🚩获取浮点值
150    /// * 🚩【2024-05-03 10:51:09】更名为`value_float`以暂时避免与「短浮点」的`value`重名
151    ///
152    /// # 📄OpenNARS
153    ///
154    /// To access the value as float
155    ///
156    /// @return The current value in float
157    #[inline(always)]
158    pub fn value_float(&self) -> Float {
159        self.value as Float * MULTIPLIER_TO_FLOAT
160    }
161
162    /// 🆕获取短整数(只读)
163    /// * 🎯用于在「其它地方的impl实现」中增强性能(直接读取内部数值)
164    #[inline(always)]
165    pub fn value_short(&self) -> UShort {
166        self.value
167    }
168
169    /// 模拟`ShortFloat.setValue`
170    /// * 🚩设置浮点值(有检查)
171    pub fn set_value(&mut self, value: Float) -> Result<(), ShortFloatError> {
172        // 转换、检查并设置值
173        self.value = Self::float_to_short_value(value)?;
174        // 返回
175        Ok(())
176    }
177
178    /// 🆕设置浮点值(无检查)
179    /// * ⚠️必须确保值在范围内
180    ///
181    /// # 📄OpenNARS
182    ///
183    /// Set new value, rounded, with validity checking
184    ///
185    /// @param v The new value
186    #[inline(always)]
187    pub fn set_value_unchecked(&mut self, value: Float) {
188        self.value = Self::float_to_short_value_unchecked(value)
189    }
190
191    /// 🆕浮点转换为「短整数」(有检查)
192    /// * 🎯提取共用逻辑,以同时用于「构造」和「赋值」
193    /// * ✅无需考虑「NaN」「无限」等值:[`Self::is_in_range`]会自动判断
194    pub fn float_to_short_value(value: Float) -> Result<UShort, ShortFloatError> {
195        match Self::is_in_range(value) {
196            // 检查通过⇒转换值
197            true => Ok(Self::float_to_short_value_unchecked(value)),
198            // 检查不通过⇒返回错误
199            false => Err(ShortFloatError::OutOfRange(value)),
200        }
201    }
202
203    /// 🆕浮点转换为「短整数」(无检查)
204    /// * 🎯提取共用逻辑,以同时用于「构造」和「赋值」
205    /// * ⚠️必须确保值在范围内
206    pub fn float_to_short_value_unchecked(value: Float) -> UShort {
207        (value * MULTIPLIER_TO_UINT).round() as UShort
208    }
209
210    // ! ✅对`equals`、`hashCode`、`clone`均已通过宏自动生成
211
212    /// 🆕判断短整数是否合法
213    /// * 🚩直接判断「是否小于等于最大值」
214    #[inline(always)]
215    pub fn is_valid_short(short: UShort) -> bool {
216        short <= SHORT_MAX
217    }
218
219    /// 🆕判断自身值是否合法
220    #[inline(always)]
221    pub fn is_valid(&self) -> bool {
222        Self::is_valid_short(self.value)
223    }
224
225    /// 🆕检查自身值是否合法
226    /// * 🚩判断自身值是否合法,然后返回[`Result`]
227    pub fn check_valid(&self) -> Result<(), ShortFloatError> {
228        match self.is_valid() {
229            true => Ok(()),
230            false => Err(ShortFloatError::OutOfRange(self.value_float())),
231        }
232    }
233
234    /// 🆕检查自身值是否合法,并返回自身
235    /// * 🚩判断自身值是否合法,然后返回[`Result<Self, ShortFloatError>`](Result)
236    /// * 🎯用于「构造后立即检查」
237    pub fn validate(self) -> Result<Self, ShortFloatError> {
238        match self.is_valid() {
239            true => Ok(self),
240            false => Err(ShortFloatError::OutOfRange(self.value_float())),
241        }
242    }
243
244    // ! ℹ️【2024-06-19 23:08:00】如下实现源自抽象的「短浮点」特征 ! //
245
246    /// 从浮点到自身转换(不检查,直接panic)
247    /// * ❌在实现[`TryFrom`]时,无法通过[`From`]实现:conflicting implementations of trait `std::convert::TryFrom<f64>` for type `entity::short_float::ShortFloat`
248    ///
249    /// ! ⚠️在「范围越界」时直接panic
250    /// * 🎯降低代码冗余量(减少过多的「错误处理」)
251    ///
252    /// ```plaintext
253    /// conflicting implementation in crate `core`:
254    /// - impl<T, U> std::convert::TryFrom<U> for T
255    /// where U: std::convert::Into<T>;
256    /// ```
257    #[inline(always)]
258    pub fn from_float(value: Float) -> Self {
259        // ! ⚠️【2024-05-02 20:41:19】直接unwrap
260        Self::try_from(value).unwrap()
261    }
262
263    #[inline(always)]
264    pub fn to_float(&self) -> Float {
265        self.value_float()
266    }
267
268    pub fn set(&mut self, new_value: Self) {
269        // self.clone_from(new_value)
270        // *self = new_value;
271        self.value = new_value.value;
272    }
273
274    pub fn is_zero(&self) -> bool {
275        self == &Self::ZERO
276    }
277
278    pub fn is_one(&self) -> bool {
279        self == &Self::ONE
280    }
281
282    pub fn is_half(&self) -> bool {
283        self == &Self::HALF
284    }
285
286    pub fn value(&self) -> Float {
287        self.to_float()
288    }
289}
290
291/// 模拟`ShortFloat.toString`、`ShortFloat.toStringBrief`
292impl ToDisplayAndBrief for ShortFloat {
293    fn to_display(&self) -> String {
294        match self.value {
295            // 对`1`的特别处理 | 🆕不同于OpenNARS:会将「异常值」按原样展示
296            SHORT_MAX => "1.0000".to_string(),
297            // 否则:右对齐,左边补零到四位,前缀添加`0.`格式化
298            value => format!("0.{value:0>4}"),
299        }
300    }
301
302    fn to_display_brief(&self) -> String {
303        // * 🚩先尝试舍入,再决定截断
304        match self.value + 50 {
305            // 对`1`的特别处理(此时可能要大于了:舍入)
306            v if v >= SHORT_MAX => "1.00".to_string(),
307            // 否则:四舍五入到百分位;右对齐,只取两位,前缀添加`0.`格式化
308            value => {
309                let s = (value / 100).to_string();
310                format!("0.{s:0>2}")
311            }
312        }
313    }
314}
315
316// 一行自动实现`Display`
317impl_display_from_to_display! { ShortFloat }
318
319/// 实现「从浮点到『短浮点』的直接转换」
320/// 🚩直接通过「构造函数+尝试转换」实现
321impl TryFrom<Float> for ShortFloat {
322    type Error = ShortFloatError;
323
324    #[inline]
325    fn try_from(value: Float) -> Result<Self, Self::Error> {
326        Ok(Self::new_unchecked(Self::float_to_short_value(value)?))
327    }
328}
329
330// 数学方法 //
331impl Add for ShortFloat {
332    type Output = Self;
333
334    /// 内部值相加,但会检查越界
335    ///
336    /// # Panics
337    ///
338    /// ! ⚠️可能会有「数值溢出」的panic
339    fn add(self, rhs: Self) -> Self::Output {
340        // 相加、构造、返回
341        Self::new(self.value + rhs.value).unwrap()
342    }
343}
344
345impl Sub for ShortFloat {
346    type Output = Self;
347
348    /// 内部值相减,无需检查越界
349    /// * 📌不会减去负值,只会「小于`0`」越界
350    ///
351    /// # Panics
352    ///
353    /// ! ⚠️可能会有「数值溢出」的panic
354    fn sub(self, rhs: Self) -> Self::Output {
355        Self::new_unchecked(self.value - rhs.value)
356    }
357}
358
359impl Mul for ShortFloat {
360    type Output = Self;
361
362    /// 内部值相乘,无需检查越界
363    /// * ✅0~1的数对乘法封闭,故无需任何检查
364    /// * ⚠️乘法在最后「除以最大值」时,采用「向下取整」的方式
365    /// * ⚠️因为乘法可能会造成上界溢出,故需要转换为「双倍位类型」
366    ///   * 🚩现在直接设置为「双倍位类型」
367    fn mul(self, rhs: Self) -> Self::Output {
368        // * 📄逻辑是 (self.value / 10000) * (rhs.value / 10000) => (new.value / 10000)
369        // * 📄实际上 (self.value / 10000) * (rhs.value / 10000) =  (new.value / 10000) / 10000
370        // * 📌因此 new.value = (self.value * rhs.value) / 10000
371        Self::new_unchecked(mul_div(self.value, rhs.value))
372    }
373}
374
375/// 相乘再归约到 0~SHORT_MAX 范围内
376/// * 🚩目前是【向下取整】归约
377fn mul_div(x: UShort, y: UShort) -> UShort {
378    (x * y) / SHORT_MAX
379}
380
381impl Div for ShortFloat {
382    type Output = Self;
383
384    /// 内部值相除,会检查越界
385    ///
386    /// # Panics
387    ///
388    /// ! ⚠️可能会有「数值溢出」的panic
389    fn div(self, rhs: Self) -> Self::Output {
390        // * 📄逻辑是 (self.value / 10000) / (rhs.value / 10000) => (new.value / 10000)
391        // * 📄实际上 (self.value / 10000) * (rhs.value / 10000) =  self.value / rhs.value
392        // * 📌因此 new.value = (self.value / rhs.value) * 10000 = (self.value * 10000) / rhs.value
393        // * 📝↑采用「先乘后除」的方法,最大保留精度
394        // 相除、构造、返回
395        Self::new((self.value * SHORT_MAX) / rhs.value).unwrap()
396    }
397}
398
399/// 整数乘法
400impl Mul<usize> for ShortFloat {
401    type Output = Self;
402
403    /// * 🚩暂且调用两次转换,在保证「使用方便」的同时保证「效果等同」
404    fn mul(self, rhs: usize) -> Self::Output {
405        Self::from_float(self.to_float().mul(rhs as Float))
406    }
407}
408
409/// 整数除法
410impl Div<usize> for ShortFloat {
411    type Output = Self;
412
413    /// * 🚩暂且调用两次转换,在保证「使用方便」的同时保证「效果等同」
414    fn div(self, rhs: usize) -> Self::Output {
415        Self::from_float(self.to_float().div(rhs as Float))
416    }
417}
418
419// NAL相关 //
420// * 🚩【2024-05-02 11:44:12】有关「真值」「预算值」的函数,均在其它文件中
421
422/// 实现「证据数值」
423impl EvidentNumber for ShortFloat {
424    #[inline(always)]
425    fn zero() -> Self {
426        Self::ZERO
427    }
428
429    #[inline(always)]
430    fn one() -> Self {
431        Self::ONE
432    }
433
434    fn root(self, n: usize) -> Self {
435        // * �【2024-05-02 18:23:31】开根不会越界,故直接`unwrap`
436        self.value_float()
437            .powf(1.0 / (n as Float))
438            .try_into()
439            .unwrap()
440    }
441}
442
443/// 实现「NAL逻辑非」
444/// ? 💭是否可以自动派生(主要是受到「孤儿规则」的限制)
445impl Not for ShortFloat {
446    type Output = Self;
447
448    fn not(self) -> Self::Output {
449        Self::ONE - self
450    }
451}
452
453/// 实现「NAL逻辑与」
454/// * 🚩【2024-05-03 11:31:18】对`clippy`允许「令人疑惑的代数实现」
455/// ? 💭是否可以自动派生(主要是受到「孤儿规则」的限制)
456#[allow(clippy::suspicious_arithmetic_impl)]
457impl BitAnd for ShortFloat {
458    type Output = Self;
459
460    fn bitand(self, rhs: Self) -> Self::Output {
461        self * rhs
462    }
463}
464
465/// 实现「NAL逻辑或」
466/// * 🚩【2024-05-03 11:31:18】对`clippy`允许「令人疑惑的代数实现」
467/// ? 💭是否可以自动派生(主要是受到「孤儿规则」的限制)
468#[allow(clippy::suspicious_arithmetic_impl)]
469impl BitOr for ShortFloat {
470    type Output = Self;
471
472    fn bitor(self, rhs: Self) -> Self::Output {
473        // a ∨ b = ¬(¬a ∧ ¬b)
474        // pipe! {
475        //     // 非
476        //     self.not()
477        //     // 与
478        //     => .and(value.not())
479        //     // 非
480        //     => .not()
481        // }
482        // !(!self & !rhs)
483        // * 🚩【2024-05-03 12:27:21】做如下代数简化,仍然能通过测试 并且结果一致
484        //   1 - (1 - a)(1 - b)
485        // = 1 - (1 - a - b + ab)
486        // = 1 - 1 + a + b - ab
487        // = a + b - ab
488        // ↑仅在`ab`引入小数,故最终舍入不会受其影响
489        Self::new_unchecked(self.value + rhs.value - ((self.value * rhs.value) / SHORT_MAX))
490    }
491}
492
493/// [「短浮点」](ShortFloat)的快捷构造宏
494#[macro_export]
495macro_rules! short_float {
496    // 从浮点数构造
497    ($float:expr) => {
498        ShortFloat::from_float($float)
499    };
500    // 从字符串构造(保留「结果」)
501    (str? $float:expr) => {
502        $s.parse::<$crate::global::Float>()
503            .map($crate::entity::ShortFloat::try_from)
504    };
505    // 从字符串构造(一路解包)
506    (str $s:expr) => {
507        $crate::entity::ShortFloat::try_from($s.parse::<$crate::global::Float>().unwrap()).unwrap()
508    };
509}
510
511/// 单元测试
512#[cfg(test)]
513mod tests {
514    use super::*;
515    use crate::{ok, util::AResult};
516    use nar_dev_utils::macro_once;
517
518    /// 用于测试的类型简写
519    type SF = ShortFloat;
520
521    // 基本功能 //
522
523    /// 📜默认浮点判等精度:1e-6
524    /// * 🎯解决「浮点判等」因精度不够失效的问题
525    const DEFAULT_EPSILON: Float = 1.0E-6;
526
527    /// 断言约等
528    /// * 🎯解决「浮点判等」因精度不够失效的问题
529    macro_rules! assert_approx_eq {
530            // * 🚩模式:@精度 值1, 值2
531            ($epsilon:expr; $v1:expr, $v2:expr) => {
532                assert!(
533                    ($v1 - $v2).abs() < $epsilon,
534                    "{} !≈ {} @ {}",
535                    $v1,
536                    $v2,
537                    $epsilon
538                )
539            };
540            ($v1:expr, $v2:expr) => {
541                assert_approx_eq!(DEFAULT_EPSILON; $v1, $v2)
542            };
543        }
544
545    /// 测试/new
546    #[test]
547    fn new() -> AResult {
548        macro_once! {
549            // * 🚩模式:短整数(作为构造函数参数)
550            macro test($( $short:expr )*) {
551                $(
552                    let _ = SF::new($short);
553                )*
554            }
555            0
556            10000
557            90
558            9000
559            1024
560            8192
561        }
562        ok!()
563    }
564
565    /// 测试/value
566    #[test]
567    fn value() -> AResult {
568        macro_once! {
569            // * 🚩模式:短整数(构造用)⇒预期值
570            macro test($( $short:expr => $expected:expr )*) {
571                $(
572                    let sf = SF::new_unchecked($short);
573                    // ! ⚠️此处必须使用「约等」判断,否则会出现`0.009 != 0.009000000000000001`的情形
574                    assert_approx_eq!(sf.value_float(), $expected);
575                )*
576            }
577            0 => 0.0
578            10000 => 1.0
579            90 => 0.009
580            9000 => 0.9
581            1024 => 0.1024
582            8192 => 0.8192
583        }
584        ok!()
585    }
586
587    // * ✅测试/is_in_range已在`float_to_short_value`中一并测试过
588
589    /// 测试/set_value
590    #[test]
591    fn set_value() -> AResult {
592        use ShortFloatError::*;
593        macro_once! {
594            // * 🚩模式:短整数(构造用) -> 浮点数(赋值用)⇒预期值(短整数) @ 返回的模式
595            macro test($( $short:literal -> $float:expr => $expected:literal @ $pattern:pat)*) {
596                $(
597                    let mut sf = SF::new_unchecked($short);
598                    let result = sf.set_value($float);
599                    // 检查返回值
600                    assert_eq!(sf.value, $expected);
601                    assert!(matches!(result, $pattern));
602                )*
603            }
604            // 正常赋值
605            0     -> 0.0                 => 0     @ Ok(..)
606            0     -> 1.0                 => 10000 @ Ok(..)
607            0     -> 0.009               => 90    @ Ok(..)
608            0     -> 0.9                 => 9000  @ Ok(..)
609            0     -> 0.1024              => 1024  @ Ok(..)
610            0     -> 0.8192              => 8192  @ Ok(..)
611            // 四舍五入
612            0     -> 0.00001             => 0     @ Ok(..)
613            0     -> 0.00002             => 0     @ Ok(..)
614            0     -> 0.00003             => 0     @ Ok(..)
615            0     -> 0.00004             => 0     @ Ok(..)
616            0     -> 0.00005             => 1     @ Ok(..)
617            0     -> 0.00006             => 1     @ Ok(..)
618            0     -> 0.00007             => 1     @ Ok(..)
619            0     -> 0.00008             => 1     @ Ok(..)
620            0     -> 0.00009             => 1     @ Ok(..)
621            // 异常赋值:超出范围
622            0     -> -0.1                => 0     @ Err(OutOfRange(..))
623            10000 ->  2.0                => 10000 @ Err(OutOfRange(..))
624            10000 -> Float::INFINITY     => 10000 @ Err(OutOfRange(..))
625            0     -> Float::NEG_INFINITY => 0     @ Err(OutOfRange(..))
626            // 异常赋值:无效值
627            10000 -> Float::NAN          => 10000 @ Err(..)
628        }
629        ok!()
630    }
631
632    /// 测试/set_value_unchecked
633    #[test]
634    fn set_value_unchecked() -> AResult {
635        macro_once! {
636            // * 🚩模式:短整数(构造用) -> 浮点数(赋值用)⇒预期值(短整数)
637            macro test($( $short:literal -> $float:expr => $expected:expr)*) {
638                $(
639                    let mut sf = SF::new_unchecked($short);
640                    sf.set_value_unchecked($float);
641                    // 检查返回值
642                    assert_eq!(sf.value, $expected, "设置值`{sf:?} -> {}`不符预期`{}`", $float, $expected);
643                )*
644            }
645            // 异常值仍可以赋值 | ⚠️负值会重置为`0`
646            0     -> 1.0001              => 10001
647            0     -> 2.0                 => 20000
648            0     -> 6.5535              => 65535
649            0     -> -0.1                => 0
650            0     -> -2.0                => 0
651            // 异常值正常四舍五入
652            0     -> 1.00001             => 10000
653            0     -> 1.00002             => 10000
654            0     -> 1.00003             => 10000
655            0     -> 1.00004             => 10000
656            0     -> 1.00005             => 10001
657            0     -> 1.00006             => 10001
658            0     -> 1.00007             => 10001
659            0     -> 1.00008             => 10001
660            0     -> 1.00009             => 10001
661            // 无穷值会被重置为 最大/最小 值:正无穷⇒最大,负无穷⇒最小
662            0     -> Float::INFINITY     => UShort::MAX
663            10000 -> Float::NEG_INFINITY => 0
664            // NaN会被重置为`0`
665            10000 -> Float::NAN          => 0
666        }
667        ok!()
668    }
669
670    // 测试/float_to_short_value
671    // * ✅已在`set_value`中连带测试过
672
673    // 测试/float_to_short_value_unchecked
674    // * ✅已在`set_value`中连带测试过
675
676    /// 测试/fmt
677    #[test]
678    fn fmt() -> AResult {
679        macro_once! {
680            // * 🚩模式:短整数(构造用) => 预期值(字符串)
681            macro test($( $short:expr => $expected:expr)*) {
682                $(
683                    let mut sf = SF::new_unchecked($short);
684                    let formatted = format!("{sf}");
685                    // 检查返回值
686                    assert_eq!(formatted, $expected);
687                )*
688            }
689            // 1
690            10000 => "1.0000"
691            // 正常
692            1024  => "0.1024"
693            8192  => "0.8192"
694            // 不足位补全
695            0     => "0.0000"
696            90    => "0.0090"
697            900   => "0.0900"
698        }
699        ok!()
700    }
701
702    /// 测试/try_from
703    #[test]
704    fn try_from() -> AResult {
705        use ShortFloatError::*;
706        macro_once! {
707            // * 🚩模式:浮点数(转换用) ⇒ 返回的模式
708            macro test($( $float:expr => $pattern:pat)*) {
709                $(
710                    // 尝试转换
711                    let mut result: ShortFloatResult = $float.try_into();
712                    // 检查返回值(兼检查转换结果)
713                    assert!(matches!(result, $pattern));
714                )*
715            }
716            // 正常转换
717            0.0                 => Ok(SF {value: 0})
718            1.0                 => Ok(SF {value: 10000})
719            0.009               => Ok(SF {value: 90})
720            0.9                 => Ok(SF {value: 9000})
721            0.1024              => Ok(SF {value: 1024})
722            0.8192              => Ok(SF {value: 8192})
723            // 四舍五入
724            0.00001             => Ok(SF {value: 0})
725            0.00002             => Ok(SF {value: 0})
726            0.00003             => Ok(SF {value: 0})
727            0.00004             => Ok(SF {value: 0})
728            0.00005             => Ok(SF {value: 1})
729            0.00006             => Ok(SF {value: 1})
730            0.00007             => Ok(SF {value: 1})
731            0.00008             => Ok(SF {value: 1})
732            0.00009             => Ok(SF {value: 1})
733            // 异常转换:超出范围
734            -0.1                => Err(OutOfRange(..))
735             2.0                => Err(OutOfRange(..))
736            Float::INFINITY     => Err(OutOfRange(..))
737            Float::NEG_INFINITY => Err(OutOfRange(..))
738            // 异常转换:无效值
739            Float::NAN          => Err(..)
740        }
741        ok!()
742    }
743
744    /// 测试/check_valid
745    #[test]
746    fn check_valid() -> AResult {
747        use ShortFloatError::*;
748        macro_once! {
749            // * 🚩模式:短整数(构造用) ⇒ 返回的模式
750            macro test($( $short:expr => $pattern:pat)*) {
751                $(
752                    // 尝试转换
753                    let sf = SF::new_unchecked($short);
754                    // 检查返回值(兼检查转换结果)
755                    assert!(matches!(sf.check_valid(), $pattern));
756                )*
757            }
758            // 正常值
759            0           => Ok(..)
760            10000       => Ok(..)
761            90          => Ok(..)
762            900         => Ok(..)
763            9000        => Ok(..)
764            1024        => Ok(..)
765            8192        => Ok(..)
766            // 异常值:超出范围
767            10001       => Err(OutOfRange(..))
768            20000       => Err(OutOfRange(..))
769            65535       => Err(OutOfRange(..))
770        }
771        ok!()
772    }
773
774    /// 快捷构造
775    macro_rules! sf {
776        ($short:expr) => {
777            SF::new_unchecked($short)
778        };
779    }
780
781    /// 测试/add
782    #[test]
783    fn add() -> AResult {
784        // 加法 | 保证 a + b <= SHORT_MAX
785        for a in 0..=SHORT_MAX {
786            for b in 0..=(SHORT_MAX - a) {
787                assert_eq!(sf!(a) + sf!(b), sf!(a + b))
788            }
789        }
790        ok!()
791    }
792
793    /// 测试/sub
794    #[test]
795    fn sub() -> AResult {
796        // 减法 | 保证 a >= b
797        for a in 0..=SHORT_MAX {
798            for b in 0..=a {
799                assert_eq!(sf!(a) - sf!(b), sf!(a - b))
800            }
801        }
802        ok!()
803    }
804
805    /// 测试/mul
806    #[test]
807    fn mul() -> AResult {
808        // 乘法
809        assert_eq!(sf!(0) * sf!(0), sf!(0));
810        assert_eq!(sf!(0) * sf!(SHORT_MAX), sf!(0));
811        assert_eq!(sf!(SHORT_MAX) * sf!(SHORT_MAX), sf!(SHORT_MAX));
812        assert_eq!(sf!(7) * sf!(9363), sf!(6)); // 边界情况:乘以的临时值`65541`溢出
813        for a in 0..=SHORT_MAX {
814            for b in 0..=SHORT_MAX {
815                assert_eq!(sf!(a) * sf!(b), sf!(a * b / SHORT_MAX))
816            }
817        }
818        ok!()
819    }
820
821    /// 测试/div
822    #[test]
823    fn div() -> AResult {
824        // 除法 | 保证 a < b
825        for a in 1..=SHORT_MAX {
826            for b in a..=SHORT_MAX {
827                assert_eq!(sf!(a) / sf!(b), sf!((a * SHORT_MAX) / b))
828            }
829        }
830        ok!()
831    }
832
833    /// 测试/to_display
834    #[test]
835    fn to_display() -> AResult {
836        macro_once! {
837            /// * 🚩模式:短浮点(浮点值) ⇒ 预期
838            macro test($( $value:tt => $expected:tt)*) {
839                $(
840                    assert_eq!(
841                        SF::from_float($value).to_display(),
842                        $expected
843                    );
844                )*
845            }
846            // 0
847            0.0    => "0.0000"
848            // 1与非1
849            1.0    => "1.0000"
850            0.9    => "0.9000"
851            // 各个位数
852            0.1    => "0.1000"
853            0.42   => "0.4200"
854            0.137  => "0.1370"
855            0.442  => "0.4420"
856            0.1024 => "0.1024"
857            0.2185 => "0.2185"
858        }
859        ok!()
860    }
861
862    /// 测试/to_display_brief
863    #[test]
864    fn to_display_brief() -> AResult {
865        macro_once! {
866            /// * 🚩模式:短浮点(浮点值) ⇒ 预期
867            macro test($( $value:tt => $expected:tt)*) {
868                $(
869                    assert_eq!(
870                        SF::from_float($value).to_display_brief(),
871                        $expected
872                    );
873                )*
874            }
875            // 0
876            0.0    => "0.00"
877            // 1与非1
878            1.0    => "1.00"
879            0.9    => "0.90"
880            // 各个位数
881            0.1    => "0.10"
882            0.42   => "0.42"
883            0.137  => "0.14" // ! 五入
884            0.442  => "0.44" // ! 四舍
885            0.1024 => "0.10" // ! 四舍
886            0.2185 => "0.22" // ! 五入
887            0.999  => "1.00" // ! 五入到`1`
888            0.9999 => "1.00" // ! 五入到`1`
889        }
890        ok!()
891    }
892}