foundry_common_fmt/
exp.rs

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