1use std::collections::VecDeque;
4
5use astro_float::{BigFloat, Sign};
6
7use crate::{BASE_10_PREC, RM};
8
9fn round_to_digit(
11 mut exponent: i32,
12 mantissa: Vec<u8>,
13 precision: usize,
14) -> Option<(i32, Vec<u8>)> {
15 let mut mantissa = VecDeque::from(mantissa);
16
17 let i = match mantissa.iter().enumerate().find(|&(_, &digit)| digit != 0) {
19 Some((i, _)) => i,
20 None => return Some((exponent, Vec::from(mantissa))),
21 };
22
23 let rounding_digit = match mantissa.get(i + precision) {
26 Some(digit) => digit,
27 None => {
28 return Some((exponent, Vec::from(mantissa)));
29 }
30 };
31 if *rounding_digit >= 5 {
32 let old_len = mantissa.len();
36 mantissa.truncate(i + precision);
37 mantissa = add1_to_vec(mantissa)?;
38 if mantissa.len() > old_len {
39 exponent += 1;
40 }
41 } else {
42 mantissa.truncate(i + precision);
45 }
46
47 let mantissa = Vec::from(mantissa);
48 Some((exponent, mantissa))
49}
50
51fn add1_to_vec(mut digits: VecDeque<u8>) -> Option<VecDeque<u8>> {
52 *digits.get_mut(digits.len() - 1)? += 1;
54
55 let mut i = digits.len() - 1;
57 while digits[i] == 10 {
58 digits[i] = 0;
59
60 if i == 0 {
61 digits.push_front(1);
63 break;
64 } else {
65 digits[i - 1] += 1;
66 i -= 1;
67 }
68 }
69
70 Some(digits)
71}
72
73fn format_num(sign: astro_float::Sign, mantissa: &[u8], mut expt: i32) -> String {
74 let mut mantissa = Vec::from(mantissa);
76
77 if mantissa.is_empty() {
79 return "0".to_string();
80 }
81
82 while *mantissa.last().unwrap() == 0 {
83 mantissa.pop();
84 }
85
86 if expt.abs() > 3 {
87 expt -= 1;
91
92 let mut bytes: Vec<u8> = Vec::new();
94
95 if sign == Sign::Neg {
97 bytes.reserve(mantissa.len() + expt.abs() as usize / 10 + 3); bytes.push(b'-');
99 } else {
100 bytes.reserve(mantissa.len() + expt.abs() as usize / 10 + 2);
101 }
102
103 bytes.push(mantissa[0] + 48);
105
106 bytes.push(b'.');
108 if mantissa.len() > 1 {
109 for d in &mantissa[1..] {
110 bytes.push(d + 48);
111 }
112 } else {
113 bytes.push(b'0');
115 }
116
117 bytes.push(b'e');
119 if expt < 0 {
121 bytes.push(b'-');
122 }
123 let mut exponent_digits: Vec<u8> =
124 expt.abs().to_string().chars().map(|d| d as u8).collect();
125 bytes.append(&mut exponent_digits);
126
127 String::from_utf8(bytes).unwrap()
128 } else {
129 let mut bytes = Vec::new();
131
132 if sign == Sign::Neg {
134 bytes.push(b'-');
135 }
136
137 if expt >= mantissa.len() as i32 {
139 for d in &mantissa {
143 bytes.push(d + 48);
144 }
145
146 for _ in 0..(expt - mantissa.len() as i32) {
148 bytes.push(b'0');
149 }
150 } else if expt <= 0 {
151 bytes.push(b'0');
154 bytes.push(b'.');
155 for _ in 0..expt.abs() {
156 bytes.push(b'0');
157 }
158 for d in mantissa {
159 bytes.push(d + 48);
160 }
161 } else {
162 for d in &mantissa[0..expt as usize] {
166 bytes.push(d + 48);
167 }
168
169 bytes.push(b'.');
171
172 for d in &mantissa[expt as usize..] {
174 bytes.push(d + 48);
175 }
176 }
177
178 String::from_utf8(bytes).unwrap()
179 }
180}
181
182pub fn float_to_string(num: &BigFloat) -> String {
184 let (s, m, e) = num.convert_to_radix(astro_float::Radix::Dec, RM).unwrap();
185 let (e, m) = round_to_digit(e, m, BASE_10_PREC).unwrap();
186 format_num(s, &m, e)
187}
188
189#[cfg(test)]
190mod tests {
191 use std::collections::VecDeque;
192
193 use astro_float::{BigFloat, Radix};
194
195 use crate::{
196 formatting::{add1_to_vec, format_num, round_to_digit},
197 PREC, RM,
198 };
199
200 #[test]
201 fn test_add1() {
202 let digits = VecDeque::from([0, 0, 0, 0, 9]);
203 let digits = add1_to_vec(digits).unwrap();
204 assert_eq!(digits, vec![0, 0, 0, 1, 0]);
205
206 let digits = VecDeque::from([0, 0, 0, 0, 5]);
207 let digits = add1_to_vec(digits).unwrap();
208 assert_eq!(digits, vec![0, 0, 0, 0, 6]);
209
210 let digits = VecDeque::from([9, 9, 9]);
211 let digits = add1_to_vec(digits).unwrap();
212 assert_eq!(digits, vec![1, 0, 0, 0]);
213 }
214
215 #[test]
216 fn test_round() {
217 let (_s, m, e) = BigFloat::from_f64(0.1 + 0.2, PREC)
220 .convert_to_radix(astro_float::Radix::Dec, RM)
221 .unwrap();
222 let (e, m) = round_to_digit(e, m, 15).unwrap();
223 assert_eq!(m, vec![3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
224 assert_eq!(e, 0);
225
226 let (_s, m, e) = BigFloat::from_f64(0.3, PREC)
227 .convert_to_radix(astro_float::Radix::Dec, RM)
228 .unwrap();
229 let (e, m) = round_to_digit(e, m, 15).unwrap();
230 assert_eq!(m, vec![3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
231 assert_eq!(e, 0);
232 }
233
234 #[test]
235 fn test_format() {
236 let (s, m, e) = BigFloat::from_f64(0.3, PREC)
237 .convert_to_radix(Radix::Dec, RM)
238 .unwrap();
239 let (e, m) = round_to_digit(e, m, 15).unwrap();
240 let str_num = format_num(s, &m, e);
241 assert_eq!(str_num, "0.3");
242 }
243}