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}