1use alloy::hex::FromHex;
2use alloy::primitives::{Bytes, B256};
3use alloy::{sol, sol_types::SolCall};
4use revm::primitives::{fixed_bytes, U256};
5use serde::{Deserialize, Serialize};
6use std::ops::{Add, Div, Mul, Neg, Sub};
7use wasm_bindgen_utils::prelude::*;
8
9#[cfg(test)]
10use alloy::primitives::aliases::I224;
11
12pub mod error;
13mod evm;
14mod fuzz_ops;
15pub mod js_api;
16pub mod tables;
17
18use error::DecimalFloatErrorSelector;
19pub use error::FloatError;
20use evm::execute_call;
21#[cfg(test)]
22use evm::execute_test_call;
23
24sol!(
25 #![sol(all_derives)]
26 DecimalFloat,
27 concat!(env!("CARGO_MANIFEST_DIR"), "/abi/DecimalFloat.json")
28);
29
30#[cfg(test)]
31sol!(
32 #![sol(all_derives)]
33 TestDecimalFloat,
34 concat!(env!("CARGO_MANIFEST_DIR"), "/abi/TestDecimalFloat.json")
35);
36
37#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, Hash)]
38#[wasm_bindgen]
39pub struct Float(B256);
40
41impl Float {
42 pub const fn from_raw(value: B256) -> Self {
44 Float(value)
45 }
46
47 pub fn get_inner(&self) -> B256 {
49 self.0
50 }
51
52 pub fn set_inner(&mut self, value: B256) {
54 self.0 = value;
55 }
56
57 pub fn from_fixed_decimal(value: U256, decimals: u8) -> Result<Self, FloatError> {
84 let calldata = DecimalFloat::fromFixedDecimalLosslessCall { value, decimals }.abi_encode();
85
86 execute_call(Bytes::from(calldata), |output| {
87 let decoded =
88 DecimalFloat::fromFixedDecimalLosslessCall::abi_decode_returns(output.as_ref())?;
89 Ok(Float(decoded))
90 })
91 }
92
93 pub fn to_fixed_decimal(self, decimals: u8) -> Result<U256, FloatError> {
118 let Float(float) = self;
119 let calldata = DecimalFloat::toFixedDecimalLosslessCall { float, decimals }.abi_encode();
120
121 execute_call(Bytes::from(calldata), |output| {
122 let decoded =
123 DecimalFloat::toFixedDecimalLosslessCall::abi_decode_returns(output.as_ref())?;
124 Ok(decoded)
125 })
126 }
127
128 pub fn from_fixed_decimal_lossy(value: U256, decimals: u8) -> Result<(Self, bool), FloatError> {
156 let calldata = DecimalFloat::fromFixedDecimalLossyCall { value, decimals }.abi_encode();
157
158 execute_call(Bytes::from(calldata), |output| {
159 let decoded =
160 DecimalFloat::fromFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?;
161 Ok((Float(decoded._0), decoded._1))
162 })
163 }
164
165 pub fn to_fixed_decimal_lossy(self, decimals: u8) -> Result<(U256, bool), FloatError> {
191 let Float(float) = self;
192 let calldata = DecimalFloat::toFixedDecimalLossyCall { float, decimals }.abi_encode();
193
194 execute_call(Bytes::from(calldata), |output| {
195 let decoded =
196 DecimalFloat::toFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?;
197 Ok((decoded._0, decoded._1))
198 })
199 }
200
201 #[cfg(test)]
228 pub fn pack_lossless(coefficient: I224, exponent: i32) -> Result<Self, FloatError> {
229 let calldata = TestDecimalFloat::packLosslessCall {
230 coefficient,
231 exponent,
232 }
233 .abi_encode();
234
235 execute_test_call(Bytes::from(calldata), |output| {
236 let decoded = TestDecimalFloat::packLosslessCall::abi_decode_returns(output.as_ref())?;
237 Ok(Float(decoded))
238 })
239 }
240
241 #[cfg(test)]
242 fn unpack(self) -> Result<(alloy::primitives::I256, alloy::primitives::I256), FloatError> {
243 let Float(float) = self;
244 let calldata = TestDecimalFloat::unpackCall { float }.abi_encode();
245
246 execute_test_call(Bytes::from(calldata), |output| {
247 let TestDecimalFloat::unpackReturn {
248 _0: coefficient,
249 _1: exponent,
250 } = TestDecimalFloat::unpackCall::abi_decode_returns(output.as_ref())?;
251
252 Ok((coefficient, exponent))
253 })
254 }
255
256 #[cfg(test)]
257 fn show_unpacked(self) -> Result<String, FloatError> {
258 let (coefficient, exponent) = self.unpack()?;
259 Ok(format!("{coefficient}e{exponent}"))
260 }
261
262 pub fn parse(str: String) -> Result<Self, FloatError> {
284 let calldata = DecimalFloat::parseCall { str }.abi_encode();
285
286 execute_call(Bytes::from(calldata), |output| {
287 let DecimalFloat::parseReturn {
288 _0: error_selector,
289 _1: parsed_float,
290 } = DecimalFloat::parseCall::abi_decode_returns(output.as_ref())?;
291
292 if error_selector != fixed_bytes!("00000000") {
293 let selector = DecimalFloatErrorSelector::try_from(error_selector);
294 return Err(FloatError::DecimalFloatSelector(selector));
295 }
296
297 Ok(Float(parsed_float))
298 })
299 }
300
301 pub fn as_hex(self) -> String {
315 alloy::hex::encode_prefixed(self.0)
316 }
317
318 pub fn from_hex(hex: &str) -> Result<Self, FloatError> {
338 let bytes = B256::from_hex(hex).map_err(|_| FloatError::InvalidHex(hex.to_string()))?;
339 Ok(Float(bytes))
340 }
341
342 pub fn max_positive_value() -> Result<Self, FloatError> {
367 let calldata = DecimalFloat::maxPositiveValueCall {}.abi_encode();
368
369 execute_call(Bytes::from(calldata), |output| {
370 let decoded = DecimalFloat::maxPositiveValueCall::abi_decode_returns(output.as_ref())?;
371 Ok(Float(decoded))
372 })
373 }
374
375 pub fn min_positive_value() -> Result<Self, FloatError> {
399 let calldata = DecimalFloat::minPositiveValueCall {}.abi_encode();
400
401 execute_call(Bytes::from(calldata), |output| {
402 let decoded = DecimalFloat::minPositiveValueCall::abi_decode_returns(output.as_ref())?;
403 Ok(Float(decoded))
404 })
405 }
406
407 pub fn max_negative_value() -> Result<Self, FloatError> {
431 let calldata = DecimalFloat::maxNegativeValueCall {}.abi_encode();
432
433 execute_call(Bytes::from(calldata), |output| {
434 let decoded = DecimalFloat::maxNegativeValueCall::abi_decode_returns(output.as_ref())?;
435 Ok(Float(decoded))
436 })
437 }
438
439 pub fn min_negative_value() -> Result<Self, FloatError> {
464 let calldata = DecimalFloat::minNegativeValueCall {}.abi_encode();
465
466 execute_call(Bytes::from(calldata), |output| {
467 let decoded = DecimalFloat::minNegativeValueCall::abi_decode_returns(output.as_ref())?;
468 Ok(Float(decoded))
469 })
470 }
471
472 pub fn zero() -> Result<Self, FloatError> {
495 let calldata = DecimalFloat::zeroCall {}.abi_encode();
496
497 execute_call(Bytes::from(calldata), |output| {
498 let decoded = DecimalFloat::zeroCall::abi_decode_returns(output.as_ref())?;
499 Ok(Float(decoded))
500 })
501 }
502
503 pub fn format_default_scientific_min() -> Result<Self, FloatError> {
523 let calldata = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MINCall {}.abi_encode();
524
525 execute_call(Bytes::from(calldata), |output| {
526 let decoded = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MINCall::abi_decode_returns(
527 output.as_ref(),
528 )?;
529 Ok(Float(decoded))
530 })
531 }
532
533 pub fn format_default_scientific_max() -> Result<Self, FloatError> {
553 let calldata = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MAXCall {}.abi_encode();
554
555 execute_call(Bytes::from(calldata), |output| {
556 let decoded = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MAXCall::abi_decode_returns(
557 output.as_ref(),
558 )?;
559 Ok(Float(decoded))
560 })
561 }
562
563 pub fn format(self) -> Result<String, FloatError> {
607 let Float(a) = self;
608 let calldata = DecimalFloat::format_1Call { a }.abi_encode();
609
610 execute_call(Bytes::from(calldata), |output| {
611 let decoded = DecimalFloat::format_1Call::abi_decode_returns(output.as_ref())?;
612 Ok(decoded)
613 })
614 }
615
616 pub fn format_with_scientific(self, scientific: bool) -> Result<String, FloatError> {
639 let Float(a) = self;
640 let calldata = DecimalFloat::format_0Call { a, scientific }.abi_encode();
641
642 execute_call(Bytes::from(calldata), |output| {
643 let decoded = DecimalFloat::format_0Call::abi_decode_returns(output.as_ref())?;
644 Ok(decoded)
645 })
646 }
647
648 pub fn format_with_range(
673 self,
674 scientific_min: Self,
675 scientific_max: Self,
676 ) -> Result<String, FloatError> {
677 let Float(a) = self;
678 let Float(scientific_min_inner) = scientific_min;
679 let Float(scientific_max_inner) = scientific_max;
680 let calldata = DecimalFloat::format_2Call {
681 a,
682 scientificMin: scientific_min_inner,
683 scientificMax: scientific_max_inner,
684 }
685 .abi_encode();
686
687 execute_call(Bytes::from(calldata), |output| {
688 let decoded = DecimalFloat::format_2Call::abi_decode_returns(output.as_ref())?;
689 Ok(decoded)
690 })
691 }
692
693 pub fn lt(self, b: Self) -> Result<bool, FloatError> {
717 let Float(a) = self;
718 let Float(b) = b;
719 let calldata = DecimalFloat::ltCall { a, b }.abi_encode();
720
721 execute_call(Bytes::from(calldata), |output| {
722 let decoded = DecimalFloat::ltCall::abi_decode_returns(output.as_ref())?;
723 Ok(decoded)
724 })
725 }
726
727 pub fn eq(self, b: Self) -> Result<bool, FloatError> {
751 let Float(a) = self;
752 let Float(b) = b;
753 let calldata = DecimalFloat::eqCall { a, b }.abi_encode();
754
755 execute_call(Bytes::from(calldata), |output| {
756 let decoded = DecimalFloat::eqCall::abi_decode_returns(output.as_ref())?;
757 Ok(decoded)
758 })
759 }
760
761 pub fn gt(self, b: Self) -> Result<bool, FloatError> {
785 let Float(a) = self;
786 let Float(b) = b;
787 let calldata = DecimalFloat::gtCall { a, b }.abi_encode();
788
789 execute_call(Bytes::from(calldata), |output| {
790 let decoded = DecimalFloat::gtCall::abi_decode_returns(output.as_ref())?;
791 Ok(decoded)
792 })
793 }
794
795 pub fn inv(self) -> Result<Self, FloatError> {
814 let Float(a) = self;
815 let calldata = DecimalFloat::invCall { a }.abi_encode();
816
817 execute_call(Bytes::from(calldata), |output| {
818 let decoded = DecimalFloat::invCall::abi_decode_returns(output.as_ref())?;
819 Ok(Float(decoded))
820 })
821 }
822
823 pub fn abs(self) -> Result<Float, FloatError> {
842 let Float(a) = self;
843 let calldata = DecimalFloat::absCall { a }.abi_encode();
844
845 execute_call(Bytes::from(calldata), |output| {
846 let decoded = DecimalFloat::absCall::abi_decode_returns(output.as_ref())?;
847 Ok(Float(decoded))
848 })
849 }
850
851 pub fn lte(self, b: Self) -> Result<bool, FloatError> {
875 let Float(a) = self;
876 let Float(b) = b;
877 let calldata = DecimalFloat::lteCall { a, b }.abi_encode();
878
879 execute_call(Bytes::from(calldata), |output| {
880 let decoded = DecimalFloat::lteCall::abi_decode_returns(output.as_ref())?;
881 Ok(decoded)
882 })
883 }
884
885 pub fn gte(self, b: Self) -> Result<bool, FloatError> {
909 let Float(a) = self;
910 let Float(b) = b;
911 let calldata = DecimalFloat::gteCall { a, b }.abi_encode();
912
913 execute_call(Bytes::from(calldata), |output| {
914 let decoded = DecimalFloat::gteCall::abi_decode_returns(output.as_ref())?;
915 Ok(decoded)
916 })
917 }
918}
919
920impl Add for Float {
921 type Output = Result<Self, FloatError>;
922
923 fn add(self, b: Self) -> Self::Output {
943 let Float(a) = self;
944 let Float(b) = b;
945 let calldata = DecimalFloat::addCall { a, b }.abi_encode();
946
947 execute_call(Bytes::from(calldata), |output| {
948 let decoded = DecimalFloat::addCall::abi_decode_returns(output.as_ref())?;
949 Ok(Float(decoded))
950 })
951 }
952}
953
954impl Sub for Float {
955 type Output = Result<Self, FloatError>;
956
957 fn sub(self, b: Self) -> Self::Output {
977 let Float(a) = self;
978 let Float(b) = b;
979 let calldata = DecimalFloat::subCall { a, b }.abi_encode();
980
981 execute_call(Bytes::from(calldata), |output| {
982 let decoded = DecimalFloat::subCall::abi_decode_returns(output.as_ref())?;
983 Ok(Float(decoded))
984 })
985 }
986}
987
988impl Mul for Float {
989 type Output = Result<Self, FloatError>;
990
991 fn mul(self, b: Self) -> Self::Output {
1011 let Float(a) = self;
1012 let Float(b) = b;
1013 let calldata = DecimalFloat::mulCall { a, b }.abi_encode();
1014
1015 execute_call(Bytes::from(calldata), |output| {
1016 let decoded = DecimalFloat::mulCall::abi_decode_returns(output.as_ref())?;
1017 Ok(Float(decoded))
1018 })
1019 }
1020}
1021
1022impl Div for Float {
1023 type Output = Result<Self, FloatError>;
1024
1025 fn div(self, b: Self) -> Self::Output {
1045 let Float(a) = self;
1046 let Float(b) = b;
1047 let calldata = DecimalFloat::divCall { a, b }.abi_encode();
1048
1049 execute_call(Bytes::from(calldata), |output| {
1050 let decoded = DecimalFloat::divCall::abi_decode_returns(output.as_ref())?;
1051 Ok(Float(decoded))
1052 })
1053 }
1054}
1055
1056impl Float {
1057 pub fn integer(self) -> Result<Float, FloatError> {
1080 let Float(a) = self;
1081 let calldata = DecimalFloat::integerCall { a }.abi_encode();
1082
1083 execute_call(Bytes::from(calldata), |output| {
1084 let decoded = DecimalFloat::integerCall::abi_decode_returns(output.as_ref())?;
1085 Ok(Float(decoded))
1086 })
1087 }
1088
1089 pub fn frac(self) -> Result<Float, FloatError> {
1108 let Float(a) = self;
1109 let calldata = DecimalFloat::fracCall { a }.abi_encode();
1110
1111 execute_call(Bytes::from(calldata), |output| {
1112 let decoded = DecimalFloat::fracCall::abi_decode_returns(output.as_ref())?;
1113 Ok(Float(decoded))
1114 })
1115 }
1116
1117 pub fn floor(self) -> Result<Float, FloatError> {
1136 let Float(a) = self;
1137 let calldata = DecimalFloat::floorCall { a }.abi_encode();
1138
1139 execute_call(Bytes::from(calldata), |output| {
1140 let decoded = DecimalFloat::floorCall::abi_decode_returns(output.as_ref())?;
1141 Ok(Float(decoded))
1142 })
1143 }
1144
1145 pub fn min(self, b: Self) -> Result<Self, FloatError> {
1169 let Float(a) = self;
1170 let Float(b) = b;
1171 let calldata = DecimalFloat::minCall { a, b }.abi_encode();
1172
1173 execute_call(Bytes::from(calldata), |output| {
1174 let decoded = DecimalFloat::minCall::abi_decode_returns(output.as_ref())?;
1175 Ok(Float(decoded))
1176 })
1177 }
1178
1179 pub fn max(self, b: Self) -> Result<Self, FloatError> {
1203 let Float(a) = self;
1204 let Float(b) = b;
1205 let calldata = DecimalFloat::maxCall { a, b }.abi_encode();
1206
1207 execute_call(Bytes::from(calldata), |output| {
1208 let decoded = DecimalFloat::maxCall::abi_decode_returns(output.as_ref())?;
1209 Ok(Float(decoded))
1210 })
1211 }
1212
1213 pub fn is_zero(self) -> Result<bool, FloatError> {
1234 let Float(a) = self;
1235 let calldata = DecimalFloat::isZeroCall { a }.abi_encode();
1236
1237 execute_call(Bytes::from(calldata), |output| {
1238 let decoded = DecimalFloat::isZeroCall::abi_decode_returns(output.as_ref())?;
1239 Ok(decoded)
1240 })
1241 }
1242}
1243
1244impl Neg for Float {
1245 type Output = Result<Self, FloatError>;
1246
1247 fn neg(self) -> Self::Output {
1266 let Float(a) = self;
1267 let calldata = DecimalFloat::minusCall { a }.abi_encode();
1268
1269 execute_call(Bytes::from(calldata), |output| {
1270 let decoded = DecimalFloat::minusCall::abi_decode_returns(output.as_ref())?;
1271 Ok(Float(decoded))
1272 })
1273 }
1274}
1275
1276impl From<B256> for Float {
1277 fn from(value: B256) -> Self {
1278 Float(value)
1279 }
1280}
1281
1282impl From<Float> for B256 {
1283 fn from(value: Float) -> Self {
1284 value.0
1285 }
1286}
1287
1288#[cfg(test)]
1289mod tests {
1290 use crate::DecimalFloat::DecimalFloatErrors;
1291
1292 use super::*;
1293 use core::str::FromStr;
1294 use proptest::prelude::*;
1295 use serde_json::json;
1296
1297 #[test]
1299 fn test_default() {
1300 let zero = Float::parse("0".to_string()).unwrap();
1301 assert!(zero.eq(Float::default()).unwrap());
1302 }
1303
1304 #[test]
1306 fn test_zero() {
1307 let zero = Float::zero().unwrap();
1308 assert!(zero.is_zero().unwrap());
1309 assert_eq!(zero.format().unwrap(), "0");
1310
1311 let parsed_zero = Float::parse("0".to_string()).unwrap();
1313 assert!(zero.eq(parsed_zero).unwrap());
1314
1315 assert!(zero.eq(Float::default()).unwrap());
1317 }
1318
1319 prop_compose! {
1320 fn arb_float()(
1321 coefficient in any::<I224>(),
1322 exponent in any::<i32>(),
1323 ) -> Float {
1324 Float::pack_lossless(coefficient, exponent).unwrap()
1325 }
1326 }
1327
1328 prop_compose! {
1329 fn reasonable_float()(
1330 int_part in -10i128.pow(18)..10i128.pow(18),
1331 decimal_part in 0u128..10u128.pow(18u32)
1332 ) -> Float {
1333 let num_str = if decimal_part == 0 {
1334 format!("{int_part}")
1335 } else {
1336 format!("{int_part}.{decimal_part}")
1337 };
1338
1339 Float::parse(num_str).unwrap()
1340 }
1341 }
1342
1343 #[test]
1345 fn test_serde() {
1346 let float = Float::parse("1.1341234234625468391".to_string()).unwrap();
1347 let serialized = serde_json::to_string(&float).unwrap();
1348 assert_eq!(
1349 serialized,
1350 json!("0xffffffed00000000000000000000000000000000000000009d642872ad59a7e7").to_string()
1351 );
1352 let deserialized: Float = serde_json::from_str(&serialized).unwrap();
1353 assert!(float.eq(deserialized).unwrap());
1354 }
1355
1356 proptest! {
1357 #[test]
1358 fn proptest_serde(float in arb_float()) {
1360 let serialized = serde_json::to_string(&float).unwrap();
1361 let deserialized: Float = serde_json::from_str(&serialized).unwrap();
1362 prop_assert!(float.eq(deserialized).unwrap());
1363 let re_serialized = serde_json::to_string(&deserialized).unwrap();
1364 prop_assert_eq!(serialized, re_serialized);
1365 }
1366 }
1367
1368 #[test]
1370 fn test_parse_empty_string_error() {
1371 let err = Float::parse("".to_string()).unwrap_err();
1372 assert!(matches!(err, FloatError::DecimalFloatSelector(_)));
1374 }
1375
1376 #[test]
1377 fn test_parse_exponent_overflow_error() {
1378 let err = Float::parse("1e3000000000".to_string()).unwrap_err();
1380 assert!(matches!(
1381 err,
1382 FloatError::DecimalFloat(DecimalFloatErrors::ExponentOverflow(_))
1383 ));
1384 }
1385
1386 #[test]
1388 fn test_parse_edge_cases() {
1389 let err = Float::parse("1.2.3".to_string()).unwrap_err();
1390 assert!(matches!(
1391 err,
1392 FloatError::DecimalFloatSelector(Err(selector))
1393 if selector == fixed_bytes!("ad384e87")
1394 ));
1395
1396 let err = Float::parse("abc".to_string()).unwrap_err();
1397 assert!(matches!(
1398 err,
1399 FloatError::DecimalFloatSelector(Err(selector))
1400 if selector == fixed_bytes!("34bd2069")
1401 ));
1402 }
1403
1404 #[test]
1407 fn test_float_constants() {
1408 let max_pos = Float::max_positive_value().unwrap();
1410 let min_pos = Float::min_positive_value().unwrap();
1411 let max_neg = Float::max_negative_value().unwrap();
1412 let min_neg = Float::min_negative_value().unwrap();
1413
1414 let zero = Float::parse("0".to_string()).unwrap();
1415
1416 assert!(!max_pos.eq(min_pos).unwrap());
1420 assert!(!max_neg.eq(min_neg).unwrap());
1421 assert!(!max_pos.eq(max_neg).unwrap());
1422 assert!(!min_pos.eq(min_neg).unwrap());
1423
1424 assert!(min_pos.gt(zero).unwrap()); assert!(max_pos.gt(zero).unwrap()); assert!(max_neg.lt(zero).unwrap()); assert!(min_neg.lt(zero).unwrap()); assert!(min_pos.lt(max_pos).unwrap()); assert!(min_neg.lt(max_neg).unwrap()); let one = Float::parse("1".to_string()).unwrap();
1436 let neg_one = Float::parse("-1".to_string()).unwrap();
1437
1438 assert!(max_pos.gt(one).unwrap());
1440 assert!(min_pos.lt(one).unwrap());
1441
1442 assert!(max_neg.gt(neg_one).unwrap());
1444 assert!(min_neg.lt(neg_one).unwrap());
1445 }
1446
1447 proptest! {
1448 #[test]
1449 fn test_format_parse(float in reasonable_float()) {
1451 let formatted = float.format().unwrap();
1452 let parsed = Float::parse(formatted.clone()).unwrap();
1453 prop_assert!(float.eq(parsed).unwrap());
1454 }
1455 }
1456
1457 proptest! {
1458 #[test]
1459 fn test_as_from_hex(float in arb_float()) {
1461 let hex = float.as_hex();
1462 let parsed = Float::from_hex(&hex).unwrap();
1463 prop_assert_eq!(parsed.as_hex(), hex);
1464 }
1465 }
1466
1467 #[test]
1469 fn test_add_exponent_overflow_error() {
1470 let max_coeff_str = "13479973333575319897333507543509815336818572211270286240551805124607";
1471 let large_coeff_i224 = I224::from_str(max_coeff_str).unwrap();
1472 let exponent_max = i32::MAX;
1473
1474 let a = Float::pack_lossless(large_coeff_i224, exponent_max).unwrap();
1475
1476 let err = (a + a).unwrap_err();
1477
1478 assert!(matches!(
1479 err,
1480 FloatError::DecimalFloat(DecimalFloatErrors::ExponentOverflow(_))
1481 ));
1482 }
1483
1484 #[test]
1486 fn test_sub_exponent_overflow_error() {
1487 let max_coeff_str = "13479973333575319897333507543509815336818572211270286240551805124607";
1488 let large_coeff_i224 = I224::from_str(max_coeff_str).unwrap();
1489 let exponent_max = i32::MAX;
1490
1491 let a = Float::pack_lossless(large_coeff_i224, exponent_max).unwrap();
1492 let b = Float::pack_lossless(-large_coeff_i224, exponent_max).unwrap();
1493
1494 let err = (b - a).unwrap_err();
1495
1496 assert!(matches!(
1497 err,
1498 FloatError::DecimalFloat(DecimalFloatErrors::ExponentOverflow(_))
1499 ));
1500 }
1501
1502 proptest! {
1503 #[test]
1504 fn test_add(a in reasonable_float(), b in reasonable_float()) {
1506 (a + b).unwrap();
1507 }
1508 }
1509
1510 proptest! {
1511 #[test]
1512 fn test_sub(a in reasonable_float(), b in reasonable_float()) {
1514 (a - b).unwrap();
1515 }
1516 }
1517
1518 proptest! {
1519 #[test]
1520 fn test_add_sub(a in reasonable_float(), b in reasonable_float()) {
1522 let sum = (a + b).unwrap();
1523 let diff = (sum - b).unwrap();
1524 prop_assert_eq!(
1525 a.format().unwrap(),
1526 diff.format().unwrap(),
1527 "a: {}, b: {}",
1528 a.format().unwrap(),
1529 b.format().unwrap(),
1530 );
1531 }
1532 }
1533
1534 #[test]
1536 fn test_lt_eq_gt() {
1537 let negone = Float::parse("-1".to_string()).unwrap();
1538 let zero = Float::parse("0".to_string()).unwrap();
1539 let three = Float::parse("3".to_string()).unwrap();
1540
1541 assert!(negone.lt(zero).unwrap());
1542 assert!(!negone.eq(zero).unwrap());
1543 assert!(!negone.gt(zero).unwrap());
1544
1545 assert!(!three.lt(zero).unwrap());
1546 assert!(!three.eq(zero).unwrap());
1547 assert!(three.gt(zero).unwrap());
1548
1549 assert!(zero.lt(three).unwrap());
1550 assert!(!zero.eq(three).unwrap());
1551 assert!(!zero.gt(three).unwrap());
1552 }
1553
1554 proptest! {
1555 #[test]
1556 fn test_lt_eq_gt_with_add(a in reasonable_float()) {
1558 let b = a;
1559 let eq = a.eq(b).unwrap();
1560 prop_assert!(eq);
1561
1562 let one = Float::parse("1".to_string()).unwrap();
1563
1564 let a = (a - one).unwrap();
1565 let lt = a.lt(b).unwrap();
1566 prop_assert!(lt);
1567
1568 let a = (a + one).unwrap();
1569 let eq = a.eq(b).unwrap();
1570 prop_assert!(eq);
1571
1572 let a = (a + one).unwrap();
1573 let gt = a.gt(b).unwrap();
1574 prop_assert!(gt);
1575 }
1576
1577 #[test]
1578 fn test_exactly_one_lt_eq_gt(a in arb_float(), b in arb_float()) {
1580 let eq = a.eq(b).unwrap();
1581 let lt = a.lt(b).unwrap();
1582 let gt = a.gt(b).unwrap();
1583
1584 let a_str = a.show_unpacked().unwrap();
1585 let b_str = b.show_unpacked().unwrap();
1586
1587 prop_assert!(lt || eq || gt, "a: {a_str}, b: {b_str}");
1588 prop_assert!(!(lt && eq), "both less than and equal: a: {a_str}, b: {b_str}");
1589 prop_assert!(!(eq && gt), "both equal and greater than: a: {a_str}, b: {b_str}");
1590 prop_assert!(!(lt && gt), "both less than and greater than: a: {a_str}, b: {b_str}");
1591 }
1592 }
1593
1594 #[test]
1596 fn test_abs() {
1597 let float = Float::parse("-3613.1324123".to_string()).unwrap();
1598 let abs = float.abs().unwrap();
1599 let formatted = abs.format().unwrap();
1600 assert_eq!(formatted, "3613.1324123");
1601
1602 let float = Float::parse("3613.1324123".to_string()).unwrap();
1603 let abs = float.abs().unwrap();
1604 let formatted = abs.format().unwrap();
1605 assert_eq!(formatted, "3613.1324123");
1606
1607 let float = Float::parse("0".to_string()).unwrap();
1608 let abs = float.abs().unwrap();
1609 let formatted = abs.format().unwrap();
1610 assert_eq!(formatted, "0");
1611 }
1612
1613 proptest! {
1614 #[test]
1615 fn test_mul(a in reasonable_float(), b in reasonable_float()) {
1617 (a * b).unwrap();
1618 }
1619 }
1620
1621 #[test]
1623 fn test_minus_format() {
1624 let float = Float::parse("-123.1234234625468391".to_string()).unwrap();
1625 let negated = float.neg().unwrap();
1626
1627 let formatted_decimal = negated.format_with_scientific(false).unwrap();
1628 assert_eq!(formatted_decimal, "123.1234234625468391");
1629
1630 let float = Float::parse("0".to_string()).unwrap();
1631 let negated = float.neg().unwrap();
1632 let formatted = negated.format().unwrap();
1633 assert_eq!(formatted, "0");
1634 }
1635
1636 proptest! {
1637 #[test]
1638 fn test_minus_minus(float in arb_float()) {
1640 let negated = float.neg().unwrap();
1641 let renegated = negated.neg().unwrap();
1642 prop_assert!(float.eq(renegated).unwrap());
1643 }
1644 }
1645
1646 proptest! {
1647 #[test]
1648 fn test_inv_prod(float in reasonable_float()) {
1650 let zero = Float::parse("0".to_string()).unwrap();
1651 prop_assume!(!float.eq(zero).unwrap());
1652
1653 let inv = float.inv().unwrap();
1654 let product = (float * inv).unwrap();
1655 let one = Float::parse("1".to_string()).unwrap();
1656
1657 let eps = Float::parse("1e-37".to_string()).unwrap();
1662 let one_plus_eps = (one + eps).unwrap();
1663 let one_minus_eps = (one - eps).unwrap();
1664
1665 let within_upper = !product.gt(one_plus_eps).unwrap();
1666 let within_lower = !product.lt(one_minus_eps).unwrap();
1667
1668 prop_assert!(
1669 within_upper && within_lower,
1670 "float: {}, inv: {}, product: {} (not within ±ε)",
1671 float.show_unpacked().unwrap(),
1672 inv.show_unpacked().unwrap(),
1673 product.show_unpacked().unwrap(),
1674 );
1675 }
1676 }
1677
1678 proptest! {
1679 #[test]
1680 fn test_abs_no_minus_sign(float in reasonable_float()) {
1682 let abs = float.abs().unwrap();
1683 let formatted = abs.format().unwrap();
1684 prop_assert!(!formatted.starts_with("-"));
1685 }
1686
1687 #[test]
1688 fn test_abs_abs(float in arb_float()) {
1690 let abs = float.abs().unwrap();
1691 let abs_abs = abs.abs().unwrap();
1692 prop_assert!(abs.eq(abs_abs).unwrap());
1693 }
1694 }
1695
1696 proptest! {
1697 #[test]
1698 fn test_div(a in reasonable_float(), b in reasonable_float()) {
1700 let zero = Float::parse("0".to_string()).unwrap();
1701 prop_assume!(!b.eq(zero).unwrap());
1702
1703 (a / b).unwrap();
1704 }
1705 }
1706
1707 prop_compose! {
1708 fn small_int_float()(int_part in -1_000_000_000_000i128..1_000_000_000_000i128) -> Float {
1709 Float::parse(int_part.to_string()).unwrap()
1710 }
1711 }
1712
1713 proptest! {
1714 #[test]
1715 fn test_mul_div_int(a in small_int_float(), b in small_int_float()) {
1717 let zero = Float::parse("0".to_string()).unwrap();
1718 prop_assume!(!b.eq(zero).unwrap());
1719
1720 let product = (a * b).unwrap();
1721 let quotient = (product / b).unwrap();
1722
1723 prop_assert!(
1724 a.eq(quotient).unwrap(),
1725 "a: {}, quotient: {}, b: {}",
1726 a.show_unpacked().unwrap(),
1727 quotient.show_unpacked().unwrap(),
1728 b.show_unpacked().unwrap()
1729 );
1730 }
1731 }
1732
1733 #[test]
1735 fn test_mul_div_manual() {
1736 let two = Float::parse("2".to_string()).unwrap();
1737 let three = Float::parse("3".to_string()).unwrap();
1738 let six = Float::parse("6".to_string()).unwrap();
1739
1740 assert!(two.eq((six / three).unwrap()).unwrap());
1741 assert!(six.eq((two * three).unwrap()).unwrap());
1742 }
1743
1744 #[test]
1746 fn test_divide_by_zero_error() {
1747 let one = Float::parse("1".to_string()).unwrap();
1748 let zero = Float::parse("0".to_string()).unwrap();
1749 let err = (one / zero).unwrap_err();
1750
1751 assert!(matches!(
1752 err,
1753 FloatError::DecimalFloat(DecimalFloatErrors::DivisionByZero(_))
1754 ));
1755 }
1756
1757 #[test]
1759 fn test_mul_exponent_overflow_error() {
1760 let near_max_exp = Float::parse("1e2147483646".to_string()).unwrap();
1761 let one_e_two = Float::parse("1e2".to_string()).unwrap();
1762
1763 let err = (near_max_exp * one_e_two).unwrap_err();
1764 assert!(matches!(
1765 err,
1766 FloatError::DecimalFloat(DecimalFloatErrors::ExponentOverflow(_))
1767 ));
1768 }
1769
1770 #[test]
1772 fn test_div_exponent_overflow_error() {
1773 let near_max_exp = Float::parse("1e2147483646".to_string()).unwrap();
1774 let one_e_neg_hundred = Float::parse("1e-100".to_string()).unwrap();
1775
1776 let err = (near_max_exp / one_e_neg_hundred).unwrap_err();
1777 assert!(matches!(
1778 err,
1779 FloatError::DecimalFloat(DecimalFloatErrors::ExponentOverflow(_))
1780 ));
1781 }
1782
1783 #[test]
1787 fn test_mul_exponent_underflow_error() {
1788 let near_min_exp = Float::parse("1e-2147483646".to_string()).unwrap();
1789 let one_e_neg_three = Float::parse("1e-3".to_string()).unwrap();
1790
1791 let err = (near_min_exp * one_e_neg_three).unwrap_err();
1792 assert!(matches!(
1793 err,
1794 FloatError::DecimalFloat(DecimalFloatErrors::ExponentUnderflow(_))
1795 ));
1796 }
1797
1798 #[test]
1800 fn test_from_fixed_decimal() {
1801 let cases = vec![
1802 (U256::from(0u128), 0u8, "0"),
1803 (U256::from(0u128), 18u8, "0"),
1804 (U256::from(1u128), 18u8, "1e-18"),
1805 (U256::from(123456789u128), 0u8, "123456789"),
1806 (U256::from(123456789u128), 2u8, "123456789e-2"),
1807 (U256::from(1000000000000000000u128), 18u8, "1"),
1808 ];
1809
1810 for (amount, decimals, expected) in cases {
1811 let float = Float::from_fixed_decimal(amount, decimals).expect("should convert");
1812 let expected = Float::parse(expected.to_string()).unwrap();
1813 assert!(float.eq(expected).unwrap());
1814 }
1815 }
1816
1817 #[test]
1819 fn test_from_fixed_decimal_err() {
1820 let err = Float::from_fixed_decimal(U256::MAX, 1).unwrap_err();
1821 assert!(matches!(
1822 err,
1823 FloatError::DecimalFloat(DecimalFloatErrors::LossyConversionToFloat(_))
1824 ));
1825 }
1826
1827 #[test]
1829 fn test_to_fixed_decimal() {
1830 let cases = vec![
1831 ("0", 0u8, 0u128),
1832 ("0", 18u8, 0u128),
1833 ("1e-18", 18u8, 1u128),
1834 ("123456789", 0u8, 123456789u128),
1835 ("123456789e-2", 2u8, 123456789u128),
1836 ("1", 18u8, 1000000000000000000u128),
1837 ];
1838
1839 for (input, decimals, expected) in cases {
1840 let float = Float::parse(input.to_string()).unwrap();
1841 let fixed = float.to_fixed_decimal(decimals).unwrap();
1842 assert_eq!(fixed, U256::from(expected));
1843 }
1844 }
1845
1846 #[test]
1848 fn test_frac_and_floor_integers() {
1849 let int_float = Float::parse("12345".to_string()).unwrap();
1850 let floor = int_float.floor().unwrap();
1851 let frac = int_float.frac().unwrap();
1852 let zero = Float::parse("0".to_string()).unwrap();
1853
1854 assert!(int_float.eq(floor).unwrap());
1855 assert!(frac.eq(zero).unwrap());
1856
1857 let int_float = Float::parse("-98765".to_string()).unwrap();
1858 let floor = int_float.floor().unwrap();
1859 let frac = int_float.frac().unwrap();
1860 let zero = Float::parse("0".to_string()).unwrap();
1861
1862 assert!(int_float.eq(floor).unwrap());
1863 assert!(frac.eq(zero).unwrap());
1864
1865 let recombined = (floor + frac).unwrap();
1866 assert!(int_float.eq(recombined).unwrap());
1867 }
1868
1869 #[test]
1871 fn test_frac_and_floor_floats() {
1872 let float = Float::parse("12345.6789".to_string()).unwrap();
1873 let floor = float.floor().unwrap();
1874 let frac = float.frac().unwrap();
1875
1876 let expected_floor = Float::parse("12345".to_string()).unwrap();
1877 let expected_frac = Float::parse("0.6789".to_string()).unwrap();
1878
1879 assert!(floor.eq(expected_floor).unwrap());
1880 assert!(frac.eq(expected_frac).unwrap());
1881 }
1882
1883 #[test]
1885 fn test_integer_positive() {
1886 let float = Float::parse("12345.6789".to_string()).unwrap();
1887 let int = float.integer().unwrap();
1888 let expected = Float::parse("12345".to_string()).unwrap();
1889 assert!(int.eq(expected).unwrap());
1890
1891 let frac = float.frac().unwrap();
1892 let recombined = (int + frac).unwrap();
1893 assert!(float.eq(recombined).unwrap());
1894 }
1895
1896 #[test]
1898 fn test_integer_negative() {
1899 let float = Float::parse("-12345.6789".to_string()).unwrap();
1900 let int = float.integer().unwrap();
1901 let frac = float.frac().unwrap();
1902
1903 let expected_int = Float::parse("-12345".to_string()).unwrap();
1905 let expected_frac = Float::parse("-0.6789".to_string()).unwrap();
1906
1907 assert!(int.eq(expected_int).unwrap());
1908 assert!(frac.eq(expected_frac).unwrap());
1909
1910 let recombined = (int + frac).unwrap();
1912 assert!(float.eq(recombined).unwrap());
1913 }
1914
1915 #[test]
1917 fn test_integer_whole_numbers() {
1918 let pos = Float::parse("42".to_string()).unwrap();
1919 assert!(pos.integer().unwrap().eq(pos).unwrap());
1920 let zero = Float::parse("0".to_string()).unwrap();
1921 assert!(pos.frac().unwrap().eq(zero).unwrap());
1922
1923 let neg = Float::parse("-42".to_string()).unwrap();
1924 assert!(neg.integer().unwrap().eq(neg).unwrap());
1925 assert!(neg.frac().unwrap().eq(zero).unwrap());
1926 }
1927
1928 proptest! {
1929 #[test]
1930 fn test_from_to_fixed_decimal_valid_range(coeff in any::<I224>(), decimals in 0u8..=66u8) {
1932 prop_assume!(coeff >= I224::ZERO);
1933
1934 let exponent = -(decimals as i32);
1935 let value = U256::from(coeff);
1936
1937 let float = Float::from_fixed_decimal(value, decimals).unwrap();
1938 let expected = Float::pack_lossless(coeff, exponent).unwrap();
1939 prop_assert!(float.eq(expected).unwrap());
1940
1941 let fixed = float.to_fixed_decimal(decimals).unwrap();
1942 assert_eq!(fixed, value);
1943 }
1944 }
1945
1946 proptest! {
1947 #[test]
1948 fn test_int_frac_properties(float in arb_float()) {
1951 let int = float.integer().unwrap();
1952 let frac = float.frac().unwrap();
1953
1954 let zero = Float::parse("0".to_string()).unwrap();
1955
1956 prop_assert!(
1957 int.frac().unwrap().eq(zero).unwrap(),
1958 "int.frac() is not zero: {}",
1959 int.show_unpacked().unwrap()
1960 );
1961
1962 prop_assert!(
1963 frac.integer().unwrap().eq(zero).unwrap(),
1964 "frac.integer() is not zero: {}",
1965 frac.show_unpacked().unwrap()
1966 );
1967
1968 let recombined = (int + frac).unwrap();
1969 prop_assert!(
1970 float.eq(recombined).unwrap(),
1971 "original: {}, int: {}, frac: {}, recombined: {}",
1972 float.show_unpacked().unwrap(),
1973 int.show_unpacked().unwrap(),
1974 frac.show_unpacked().unwrap(),
1975 recombined.show_unpacked().unwrap()
1976 );
1977
1978 let one = Float::parse("1".to_string()).unwrap();
1979 let neg_one = one.neg().unwrap();
1980 prop_assert!(
1981 frac.lt(one).unwrap(),
1982 "frac not < 1: {}",
1983 frac.show_unpacked().unwrap()
1984 );
1985 prop_assert!(
1986 frac.gt(neg_one).unwrap(),
1987 "frac not > -1: {}",
1988 frac.show_unpacked().unwrap()
1989 );
1990 }
1991 }
1992
1993 #[test]
1995 fn test_min_max_manual() {
1996 let negone = Float::parse("-1".to_string()).unwrap();
1997 let zero = Float::parse("0".to_string()).unwrap();
1998 let three = Float::parse("3".to_string()).unwrap();
1999 let seven = Float::parse("7".to_string()).unwrap();
2000
2001 assert!(negone.eq(negone.min(zero).unwrap()).unwrap());
2003 assert!(negone.eq(negone.min(three).unwrap()).unwrap());
2004 assert!(zero.eq(zero.min(three).unwrap()).unwrap());
2005 assert!(seven.eq(seven.min(seven).unwrap()).unwrap());
2007
2008 assert!(zero.eq(negone.max(zero).unwrap()).unwrap());
2010 assert!(three.eq(negone.max(three).unwrap()).unwrap());
2011 assert!(three.eq(zero.max(three).unwrap()).unwrap());
2012 assert!(seven.eq(seven.max(seven).unwrap()).unwrap());
2014 }
2015
2016 #[test]
2018 fn test_is_zero_manual() {
2019 let zero = Float::parse("0".to_string()).unwrap();
2020 assert!(zero.is_zero().unwrap());
2021
2022 let neg_zero = Float::parse("-0".to_string()).unwrap();
2024 assert!(neg_zero.is_zero().unwrap());
2025 let zero_point = Float::parse("0.0".to_string()).unwrap();
2026 assert!(zero_point.is_zero().unwrap());
2027
2028 let one = Float::parse("1".to_string()).unwrap();
2029 assert!(!one.is_zero().unwrap());
2030 }
2031
2032 proptest! {
2033 #[test]
2034 fn test_min_max_properties(a in reasonable_float(), b in reasonable_float()) {
2037 let min = a.min(b).unwrap();
2038 let max = a.max(b).unwrap();
2039
2040 prop_assert!(
2041 !min.gt(a).unwrap(),
2042 "min > a: min={}, a={}",
2043 min.show_unpacked().unwrap(),
2044 a.show_unpacked().unwrap()
2045 );
2046 prop_assert!(
2047 !min.gt(b).unwrap(),
2048 "min > b: min={}, b={}",
2049 min.show_unpacked().unwrap(),
2050 b.show_unpacked().unwrap()
2051 );
2052
2053 prop_assert!(
2054 !max.lt(a).unwrap(),
2055 "max < a: max={}, a={}",
2056 max.show_unpacked().unwrap(),
2057 a.show_unpacked().unwrap()
2058 );
2059 prop_assert!(
2060 !max.lt(b).unwrap(),
2061 "max < b: max={}, b={}",
2062 max.show_unpacked().unwrap(),
2063 b.show_unpacked().unwrap()
2064 );
2065
2066 let min_is_a = min.eq(a).unwrap();
2067 let min_is_b = min.eq(b).unwrap();
2068 prop_assert!(
2069 min_is_a || min_is_b,
2070 "min is not equal to either operand: a={}, b={}, min={}",
2071 a.show_unpacked().unwrap(),
2072 b.show_unpacked().unwrap(),
2073 min.show_unpacked().unwrap()
2074 );
2075
2076 let max_is_a = max.eq(a).unwrap();
2077 let max_is_b = max.eq(b).unwrap();
2078 prop_assert!(
2079 max_is_a || max_is_b,
2080 "max is not equal to either operand: a={}, b={}, max={}",
2081 a.show_unpacked().unwrap(),
2082 b.show_unpacked().unwrap(),
2083 max.show_unpacked().unwrap()
2084 );
2085
2086 prop_assert!(
2087 !min.gt(max).unwrap(),
2088 "min > max: min={}, max={}",
2089 min.show_unpacked().unwrap(),
2090 max.show_unpacked().unwrap()
2091 );
2092 }
2093 }
2094
2095 #[test]
2097 fn test_lte_gte() {
2098 let negone = Float::parse("-1".to_string()).unwrap();
2099 let zero = Float::parse("0".to_string()).unwrap();
2100 let three = Float::parse("3".to_string()).unwrap();
2101
2102 assert!(negone.lte(zero).unwrap());
2103 assert!(zero.lte(three).unwrap());
2104 assert!(negone.lte(three).unwrap());
2105
2106 assert!(zero.gte(negone).unwrap());
2107 assert!(three.gte(zero).unwrap());
2108 assert!(three.gte(negone).unwrap());
2109 }
2110
2111 proptest! {
2112 #[test]
2113 fn test_lte_gte_fuzz(a in reasonable_float()) {
2115 let b = a;
2116 let one = Float::parse("1".to_string()).unwrap();
2117
2118 let a = (a - one).unwrap();
2119 let lte = a.lte(b).unwrap();
2120 prop_assert!(lte); let a = (a + one).unwrap();
2123 let gte = a.gte(b).unwrap();
2124 let lte = a.lte(b).unwrap();
2125 prop_assert!(gte); prop_assert!(lte); let a = (a + one).unwrap();
2129 let gte = a.gte(b).unwrap();
2130 prop_assert!(gte); }
2132 }
2133
2134 #[test]
2136 fn test_from_fixed_decimal_lossy() {
2137 let lossless_cases = vec![
2139 (U256::from(0u128), 0u8, "0"),
2140 (U256::from(0u128), 18u8, "0"),
2141 (U256::from(1u128), 18u8, "1e-18"),
2142 (U256::from(123456789u128), 0u8, "123456789"),
2143 (U256::from(123456789u128), 2u8, "123456789e-2"),
2144 (U256::from(1000000000000000000u128), 18u8, "1"),
2145 ];
2146
2147 for (amount, decimals, expected) in lossless_cases {
2148 let (float, lossless) =
2149 Float::from_fixed_decimal_lossy(amount, decimals).expect("should convert");
2150 let expected = Float::parse(expected.to_string()).unwrap();
2151 assert!(float.eq(expected).unwrap());
2152 assert!(
2153 lossless,
2154 "conversion should be lossless for amount={}, decimals={}",
2155 amount, decimals
2156 );
2157 }
2158
2159 let (float, lossless) = Float::from_fixed_decimal_lossy(U256::MAX, 1).unwrap();
2161 assert!(!lossless, "U256::MAX conversion should be lossy");
2162 assert!(!float.is_zero().unwrap(), "result should not be zero");
2163 }
2164
2165 #[test]
2167 fn test_to_fixed_decimal_lossy() {
2168 let lossy_cases = vec![
2170 (U256::from(1), 18u8, 0u128),
2171 (U256::from(123456789), 0u8, 12345678u128),
2172 (U256::from(123456789), 2u8, 12345678u128),
2173 ];
2174
2175 for (input, decimals, expected) in lossy_cases {
2176 let float = Float::from_fixed_decimal(input, decimals + 1).unwrap();
2177 let (fixed, lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2178 assert_eq!(
2179 fixed,
2180 U256::from(expected),
2181 "wrong value for input={}, decimals={}",
2182 input,
2183 decimals
2184 );
2185 assert!(
2186 !lossless,
2187 "should be lossy for input={}, decimals={}",
2188 input, decimals
2189 );
2190 }
2191
2192 let lossless_cases = vec![
2194 (U256::from(0), 0u8, 0u128),
2196 (U256::from(0), 18u8, 0u128),
2197 (U256::from(12340), 3u8, 1234u128),
2199 ];
2200
2201 for (input, decimals, expected) in lossless_cases {
2202 let float = Float::from_fixed_decimal(input, decimals + 1).unwrap();
2203 let (fixed, lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2204 assert_eq!(
2205 fixed,
2206 U256::from(expected),
2207 "wrong value for input={}, decimals={}",
2208 input,
2209 decimals
2210 );
2211 assert!(
2212 lossless,
2213 "should be lossless for input={}, decimals={}",
2214 input, decimals
2215 );
2216 }
2217 }
2218
2219 proptest! {
2220 #[test]
2221 fn test_from_to_fixed_decimal_lossy_valid_range(coeff in any::<I224>(), decimals in 0u8..=66u8) {
2224 prop_assume!(coeff >= I224::ZERO);
2225
2226 let exponent = -(decimals as i32 + 1);
2227 let value = U256::from(coeff);
2228
2229 let (float, from_lossless) = Float::from_fixed_decimal_lossy(value, decimals + 1).unwrap();
2230 let expected = Float::pack_lossless(coeff, exponent).unwrap();
2231 prop_assert!(float.eq(expected).unwrap());
2232
2233 prop_assert!(from_lossless, "from_fixed_decimal_lossy should be lossless for coeff={coeff}");
2235
2236 let (fixed, to_lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2237 assert_eq!(fixed, value / U256::from(10));
2238
2239 if value == U256::ZERO || value % U256::from(10) == U256::ZERO {
2242 prop_assert!(to_lossless, "to_fixed_decimal_lossy should be lossless when last digit is 0: value={}", value);
2243 } else {
2244 prop_assert!(!to_lossless, "to_fixed_decimal_lossy should be lossy when losing precision: value={}", value);
2245 }
2246 }
2247 }
2248
2249 proptest! {
2250 #[test]
2251 fn test_constants_relationships(float in reasonable_float()) {
2254 let max_pos = Float::max_positive_value().unwrap();
2255 let min_pos = Float::min_positive_value().unwrap();
2256 let max_neg = Float::max_negative_value().unwrap();
2257 let min_neg = Float::min_negative_value().unwrap();
2258 let zero = Float::parse("0".to_string()).unwrap();
2259
2260 if float.gt(zero).unwrap() {
2263 prop_assert!(float.lte(max_pos).unwrap());
2264 prop_assert!(float.gte(min_pos).unwrap());
2265 }
2266
2267 if float.lt(zero).unwrap() {
2270 prop_assert!(float.lte(max_neg).unwrap());
2271 prop_assert!(float.gte(min_neg).unwrap());
2272 }
2273
2274 prop_assert!(max_pos.gt(zero).unwrap());
2276 prop_assert!(min_pos.gt(zero).unwrap());
2277 prop_assert!(max_neg.lt(zero).unwrap());
2278 prop_assert!(min_neg.lt(zero).unwrap());
2279
2280 prop_assert!(min_pos.lt(max_pos).unwrap());
2282 prop_assert!(min_neg.lt(max_neg).unwrap());
2283 prop_assert!(max_neg.lt(zero).unwrap());
2284 prop_assert!(min_pos.gt(zero).unwrap());
2285 }
2286 }
2287
2288 proptest! {
2289 #[test]
2290 fn test_constants_edge_cases(float in arb_float()) {
2292 let max_pos = Float::max_positive_value().unwrap();
2293 let min_pos = Float::min_positive_value().unwrap();
2294 let max_neg = Float::max_negative_value().unwrap();
2295 let min_neg = Float::min_negative_value().unwrap();
2296
2297 prop_assert!(!max_pos.eq(min_pos).unwrap());
2299 prop_assert!(!max_neg.eq(min_neg).unwrap());
2300 prop_assert!(!max_pos.eq(max_neg).unwrap());
2301 prop_assert!(!min_pos.eq(min_neg).unwrap());
2302
2303 if !float.eq(max_pos).unwrap() {
2309 prop_assert!(!float.gt(max_pos).unwrap());
2310 }
2311 if !float.eq(min_neg).unwrap() {
2312 prop_assert!(!float.lt(min_neg).unwrap());
2313 }
2314 }
2315 }
2316}