foundry_common_fmt/
exp.rs

1use alloy_primitives::{Sign, I256, U256};
2use yansi::Paint;
3
4/// Returns the number expressed as a string in exponential notation
5/// with the given precision (number of significant figures),
6/// optionally removing trailing zeros from the mantissa.
7///
8/// Examples:
9///
10/// ```text
11/// precision = 4, trim_end_zeroes = false
12///     1234124124 -> 1.234e9
13///     10000000 -> 1.000e7
14/// precision = 3, trim_end_zeroes = true
15///     1234124124 -> 1.23e9
16///     10000000 -> 1e7
17/// ```
18#[inline]
19pub fn to_exp_notation(value: U256, precision: usize, trim_end_zeros: bool, sign: Sign) -> String {
20    let stringified = value.to_string();
21    let exponent = stringified.len() - 1;
22    let mut mantissa = stringified.chars().take(precision).collect::<String>();
23
24    // optionally remove trailing zeros
25    if trim_end_zeros {
26        mantissa = mantissa.trim_end_matches('0').to_string();
27    }
28
29    // Place a decimal point only if needed
30    // e.g. 1234 -> 1.234e3 (needed)
31    //      5 -> 5 (not needed)
32    if mantissa.len() > 1 {
33        mantissa.insert(1, '.');
34    }
35
36    format!("{sign}{mantissa}e{exponent}")
37}
38
39/// Formats a U256 number to string, adding an exponential notation _hint_ if it
40/// is larger than `10_000`, with a precision of `4` figures, and trimming the
41/// trailing zeros.
42///
43/// # Examples
44///
45/// ```
46/// use alloy_primitives::U256;
47/// use foundry_common_fmt::format_uint_exp as f;
48///
49/// # yansi::disable();
50/// assert_eq!(f(U256::from(0)), "0");
51/// assert_eq!(f(U256::from(1234)), "1234");
52/// assert_eq!(f(U256::from(1234567890)), "1234567890 [1.234e9]");
53/// assert_eq!(f(U256::from(1000000000000000000_u128)), "1000000000000000000 [1e18]");
54/// assert_eq!(f(U256::from(10000000000000000000000_u128)), "10000000000000000000000 [1e22]");
55/// ```
56pub fn format_uint_exp(num: U256) -> String {
57    if num < U256::from(10_000) {
58        return num.to_string()
59    }
60
61    let exp = to_exp_notation(num, 4, true, Sign::Positive);
62    format!("{num} {}", format!("[{exp}]").dim())
63}
64
65/// Formats a U256 number to string, adding an exponential notation _hint_.
66///
67/// Same as [`format_uint_exp`].
68///
69/// # Examples
70///
71/// ```
72/// use alloy_primitives::I256;
73/// use foundry_common_fmt::format_int_exp as f;
74///
75/// # yansi::disable();
76/// assert_eq!(f(I256::try_from(0).unwrap()), "0");
77/// assert_eq!(f(I256::try_from(-1).unwrap()), "-1");
78/// assert_eq!(f(I256::try_from(1234).unwrap()), "1234");
79/// assert_eq!(f(I256::try_from(1234567890).unwrap()), "1234567890 [1.234e9]");
80/// assert_eq!(f(I256::try_from(-1234567890).unwrap()), "-1234567890 [-1.234e9]");
81/// assert_eq!(f(I256::try_from(1000000000000000000_u128).unwrap()), "1000000000000000000 [1e18]");
82/// assert_eq!(
83///     f(I256::try_from(10000000000000000000000_u128).unwrap()),
84///     "10000000000000000000000 [1e22]"
85/// );
86/// assert_eq!(
87///     f(I256::try_from(-10000000000000000000000_i128).unwrap()),
88///     "-10000000000000000000000 [-1e22]"
89/// );
90/// ```
91pub fn format_int_exp(num: I256) -> String {
92    let (sign, abs) = num.into_sign_and_abs();
93    if abs < U256::from(10_000) {
94        return format!("{sign}{abs}");
95    }
96
97    let exp = to_exp_notation(abs, 4, true, sign);
98    format!("{sign}{abs} {}", format!("[{exp}]").dim())
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_format_to_exponential_notation() {
107        let value = 1234124124u64;
108
109        let formatted = to_exp_notation(U256::from(value), 4, false, Sign::Positive);
110        assert_eq!(formatted, "1.234e9");
111
112        let formatted = to_exp_notation(U256::from(value), 3, true, Sign::Positive);
113        assert_eq!(formatted, "1.23e9");
114
115        let value = 10000000u64;
116
117        let formatted = to_exp_notation(U256::from(value), 4, false, Sign::Positive);
118        assert_eq!(formatted, "1.000e7");
119
120        let formatted = to_exp_notation(U256::from(value), 3, true, Sign::Positive);
121        assert_eq!(formatted, "1e7");
122    }
123}