1const EXP_MASK: u64 = 0x7FF0_0000_0000_0000;
9
10const FIRST_ONE: u64 = 0x8000_0000_0000_0000;
12
13const NEG_ONE: u64 = 0xFFFF_FFFF_FFFF_FFFF;
15
16#[derive(Debug, Clone)]
23pub struct PrecisionBound {
24 position: u64,
26 precision: f64,
28 precision_exp: i32,
30 int_length: u64,
32 decimal_length: u64,
34}
35
36impl PrecisionBound {
37 pub fn new(precision: f64) -> Self {
49 let xu = precision.to_bits();
50 let precision_exp = ((xu & EXP_MASK) >> 52) as i32 - 1023;
51
52 PrecisionBound {
53 position: 0,
54 precision,
55 precision_exp,
56 int_length: 0,
57 decimal_length: 0,
58 }
59 }
60
61 pub fn precision_bound(&mut self, orig: f64) -> f64 {
66 let mask = !0u64;
67 let origu = orig.to_bits();
68
69 let mut curu = origu & (mask << self.position) | (1u64 << self.position);
70 let mut cur = f64::from_bits(curu);
71 let mut pre = cur;
72 let bounded = self.is_bounded(orig, cur);
73
74 if bounded {
75 loop {
77 if self.position == 52 {
78 return pre;
79 }
80 self.position += 1;
81 curu = origu & (mask << self.position) | (1u64 << self.position);
82 cur = f64::from_bits(curu);
83 if !self.is_bounded(orig, cur) {
84 break;
85 }
86 pre = cur;
87 }
88 } else {
89 loop {
91 if self.position == 0 {
92 break;
93 }
94 self.position -= 1;
95 curu = origu & (mask << self.position) | (1u64 << self.position);
96 cur = f64::from_bits(curu);
97 if self.is_bounded(orig, cur) {
98 pre = cur;
99 break;
100 }
101 }
102 }
103 pre
104 }
105
106 pub fn cal_length(&mut self, x: f64) {
108 let xu = x.to_bits();
109 let trailing_zeros = xu.trailing_zeros();
110 let exp = ((xu & EXP_MASK) >> 52) as i32 - 1023;
111
112 let mut dec_length = 0u64;
113 if trailing_zeros >= 52 {
114 if exp < 0 {
115 dec_length = (-exp) as u64;
116 if exp < self.precision_exp {
117 dec_length = 0;
118 }
119 }
120 } else if (52 - trailing_zeros as i32) > exp {
121 dec_length = ((52 - trailing_zeros as i32) - exp) as u64;
122 }
123
124 if exp >= 0 && (exp + 1) as u64 > self.int_length {
125 self.int_length = (exp + 1) as u64;
126 }
127 if dec_length > self.decimal_length {
128 self.decimal_length = dec_length;
129 }
130 }
131
132 pub fn get_length(&self) -> (u64, u64) {
134 (self.int_length, self.decimal_length)
135 }
136
137 #[inline]
139 pub fn set_length(&mut self, ilen: u64, dlen: u64) {
140 self.int_length = ilen;
141 self.decimal_length = dlen;
142 }
143
144 #[inline]
146 pub fn fetch_components(&self, bd: f64) -> (i64, u64) {
147 let bdu = bd.to_bits();
148 let exp = ((bdu & EXP_MASK) >> 52) as i32 - 1023;
149 let sign = bdu & FIRST_ONE;
150 let mut int_part = 0u64;
151 let mut dec_part: u64;
152
153 if exp >= 0 {
154 dec_part = bdu << (12 + exp) as u64;
155 int_part = ((bdu << 11) | FIRST_ONE) >> (63 - exp) as u64;
156 if sign != 0 {
157 int_part = !int_part;
158 dec_part = (!dec_part).wrapping_add(1);
159 }
160 } else if exp < self.precision_exp {
161 dec_part = 0u64;
162 if sign != 0 {
163 int_part = NEG_ONE;
164 dec_part = !dec_part;
165 }
166 } else {
167 dec_part = ((bdu << 11) | FIRST_ONE) >> ((-exp - 1) as u64);
168 if sign != 0 {
169 int_part = NEG_ONE;
170 dec_part = !dec_part;
171 }
172 }
173
174 let signed_int = int_part as i64;
175 (signed_int, dec_part >> (64u64 - self.decimal_length))
176 }
177
178 #[inline]
183 pub fn fetch_fixed_aligned(&self, bd: f64) -> i64 {
184 let bdu = bd.to_bits();
185 let exp = ((bdu & EXP_MASK) >> 52) as i32 - 1023;
186 let sign = bdu & FIRST_ONE;
187 let mut fixed: u64;
188
189 if exp < self.precision_exp {
190 fixed = 0u64;
191 } else {
192 fixed = ((bdu << 11) | FIRST_ONE) >> (63 - exp - self.decimal_length as i32) as u64;
193 if sign != 0 {
194 fixed = !(fixed.wrapping_sub(1));
195 }
196 }
197
198 fixed as i64
199 }
200
201 #[inline]
203 pub fn is_bounded(&self, a: f64, b: f64) -> bool {
204 (a - b).abs() < self.precision
205 }
206}
207
208pub fn get_precision_bound(precision: i32) -> f64 {
216 if precision <= 0 {
217 return 0.49;
218 }
219
220 let mut s = String::from("0.");
221 for _ in 0..precision {
222 s.push('0');
223 }
224 s.push_str("49");
225 s.parse().unwrap_or(0.49)
226}
227
228pub const PRECISION_MAP: [(i32, u64); 16] = [
232 (0, 0),
233 (1, 4),
234 (2, 7),
235 (3, 10),
236 (4, 14),
237 (5, 17),
238 (6, 20),
239 (7, 24),
240 (8, 27),
241 (9, 30),
242 (10, 34),
243 (11, 37),
244 (12, 40),
245 (13, 44),
246 (14, 47),
247 (15, 50),
248];
249
250pub fn get_decimal_length(precision: i32) -> u64 {
252 PRECISION_MAP
253 .iter()
254 .find(|(p, _)| *p == precision)
255 .map(|(_, len)| *len)
256 .unwrap_or(0)
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_precision_bound_creation() {
265 let bound = PrecisionBound::new(0.00005);
266 assert!(bound.precision > 0.0);
267 }
268
269 #[test]
270 fn test_get_precision_bound() {
271 let pb = get_precision_bound(4);
272 assert!(pb > 0.0 && pb < 0.001);
273 }
274
275 #[test]
276 fn test_is_bounded() {
277 let bound = PrecisionBound::new(0.05);
278 assert!(bound.is_bounded(1.0, 1.01));
279 assert!(!bound.is_bounded(1.0, 1.1));
280 }
281
282 #[test]
283 fn test_precision_map() {
284 assert_eq!(get_decimal_length(0), 0);
285 assert_eq!(get_decimal_length(4), 14);
286 assert_eq!(get_decimal_length(10), 34);
287 }
288
289 #[test]
290 fn test_fetch_fixed_aligned() {
291 let mut bound = PrecisionBound::new(0.00005);
292 bound.set_length(4, 14);
293
294 let fixed = bound.fetch_fixed_aligned(3.14159);
295 assert!(fixed > 0);
296 }
297}