Skip to main content

zenith_float_num/common/
util.rs

1//! Auxiliary functions.
2
3use crate::{
4    defs::{Error, Word, WORD_BIT_SIZE, WORD_MAX, WORD_SIGNIFICANT_BIT},
5    RoundingMode,
6};
7
8#[cfg(test)]
9use crate::{num::ExactNumNumber, Sign, EXPONENT_MIN};
10
11#[cfg(test)]
12#[cfg(not(feature = "std"))]
13use alloc::vec::Vec;
14
15/// integer logarithm base 2 of a number.
16pub fn log2_ceil(mut n: usize) -> usize {
17    let mut ret = 0;
18    let mut sticky = 0;
19    while n > 1 {
20        if n & 1 != 0 {
21            sticky = 1;
22        }
23        ret += 1;
24        n >>= 1;
25    }
26    ret + sticky
27}
28
29/// integer logarithm base 2 of a number.
30pub fn log2_floor(mut n: usize) -> usize {
31    let mut ret = 0;
32    while n > 1 {
33        ret += 1;
34        n >>= 1;
35    }
36    ret
37}
38
39/// square root integer approximation.
40pub fn sqrt_int(a: u32) -> u32 {
41    let a = a as u64;
42    let mut x = a;
43    for _ in 0..20 {
44        if x == 0 {
45            break;
46        }
47        x = (a / x + x) >> 1;
48    }
49    x as u32
50}
51
52// cost of multiplication of two numbers with precision p.
53pub fn calc_mul_cost(p: usize) -> usize {
54    if p < 70 {
55        p * p
56    } else {
57        // toom-3
58        if p < 1625 {
59            sqrt_int((p * p * p) as u32) as usize
60        } else {
61            let q = sqrt_int(p as u32) as usize;
62            q * q * q
63        }
64    }
65}
66
67// cost of addition/subtraction of two numbers with precision p.
68#[inline]
69pub fn calc_add_cost(p: usize) -> usize {
70    p
71}
72
73/// Maximum extra correct-rounding retries.
74pub const MAX_PREC_RETRY: usize = 256;
75
76#[cfg(test)]
77pub const MAX_DEC_SCALE: usize = 1_000_000;
78
79/// True when working precision has grown too far past the requested precision.
80#[inline]
81pub fn prec_retry_exhausted(p_wrk: usize, p: usize) -> bool {
82    p_wrk > p.saturating_add(WORD_BIT_SIZE.saturating_mul(MAX_PREC_RETRY))
83}
84
85/// Bump working precision for a correct-rounding retry, or error instead of spinning.
86#[inline]
87pub fn bump_prec_retry(p_wrk: &mut usize, p_inc: &mut usize, p: usize) -> Result<(), Error> {
88    if prec_retry_exhausted(*p_wrk, p) {
89        return Err(Error::PrecisionRetryExhausted);
90    }
91    *p_wrk += *p_inc;
92    *p_inc = round_p(*p_wrk / 5);
93    Ok(())
94}
95
96#[cfg(test)]
97pub const TEST_EXP_BOUND: crate::Exponent = 1024;
98
99#[cfg(test)]
100pub fn test_loop_count(full: usize) -> usize {
101    full.min(200)
102}
103
104// Estimate of sqrt op cost.
105#[inline]
106pub fn calc_sqrt_cost(p: usize, cost_mul: usize, cost_add: usize) -> usize {
107    let log3_estimate = (log2_floor(p) * 41349) >> 16;
108    log3_estimate * (5 * cost_mul + 2 * cost_add) / 2
109}
110
111#[inline(always)]
112pub fn add_carry(a: Word, b: Word, c: Word, r: &mut Word) -> Word {
113    #[cfg(target_arch = "x86_64")]
114    {
115        // platform-specific operation
116        core::arch::x86_64::_addcarry_u64(c as u8, a, b, r) as Word
117    }
118
119    #[cfg(target_arch = "x86")]
120    {
121        // platform-specific operation
122        core::arch::x86::_addcarry_u32(c as u8, a, b, r) as Word
123    }
124
125    #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
126    {
127        use crate::defs::DoubleWord;
128        use crate::defs::WORD_BASE;
129
130        let mut s = c as DoubleWord + a as DoubleWord + b as DoubleWord;
131        if s >= WORD_BASE {
132            s -= WORD_BASE;
133            *r = s as Word;
134            1
135        } else {
136            *r = s as Word;
137            0
138        }
139    }
140}
141
142#[inline(always)]
143pub fn sub_borrow(a: Word, b: Word, c: Word, r: &mut Word) -> Word {
144    #[cfg(target_arch = "x86_64")]
145    {
146        // platform-specific operation
147        core::arch::x86_64::_subborrow_u64(c as u8, a, b, r) as Word
148    }
149
150    #[cfg(target_arch = "x86")]
151    {
152        // platform-specific operation
153        core::arch::x86::_subborrow_u32(c as u8, a, b, r) as Word
154    }
155
156    #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
157    {
158        use crate::defs::DoubleWord;
159        use crate::defs::WORD_BASE;
160
161        let v1 = a as DoubleWord;
162        let v2 = b as DoubleWord + c as DoubleWord;
163
164        if v1 < v2 {
165            *r = (v1 + WORD_BASE - v2) as Word;
166            1
167        } else {
168            *r = (v1 - v2) as Word;
169            0
170        }
171    }
172}
173
174// Shift m left by n digits.
175pub fn shift_slice_left(m: &mut [Word], n: usize) {
176    let idx = n / WORD_BIT_SIZE;
177    let shift = n % WORD_BIT_SIZE;
178    if idx >= m.len() {
179        m.fill(0);
180    } else if shift > 0 {
181        let l = m.len() - 1;
182        let end = m.as_mut_ptr();
183        unsafe {
184            let mut dst = end.add(l);
185            let mut src = end.add(l - idx);
186            loop {
187                if src > end {
188                    let mut d = *src << shift;
189                    src = src.sub(1);
190                    d |= *src >> (WORD_BIT_SIZE - shift);
191                    *dst = d;
192                    dst = dst.sub(1);
193                } else {
194                    break;
195                }
196            }
197            *dst = *src << shift;
198        }
199        m[0..idx].fill(0);
200    } else if idx > 0 {
201        let r = m.len() - idx;
202        m.copy_within(0..r, idx);
203        m[..idx].fill(0);
204    }
205}
206
207// Shift m left by n digits and put result in m2.
208pub fn shift_slice_left_copy(m: &[Word], m2: &mut [Word], n: usize) {
209    let idx = n / WORD_BIT_SIZE;
210    let shift = n % WORD_BIT_SIZE;
211    if idx >= m2.len() {
212        m2.fill(0);
213    } else if shift > 0 {
214        m2[..idx].fill(0);
215        let mut dst = m2[idx..].iter_mut();
216        let src = m.iter();
217        let mut prev = 0;
218        for (a, b) in src.zip(dst.by_ref()) {
219            *b = (prev >> (WORD_BIT_SIZE - shift)) | (*a << shift);
220            prev = *a;
221        }
222        if let Some(b) = dst.next() {
223            *b = prev >> (WORD_BIT_SIZE - shift);
224        }
225        for b in dst {
226            *b = 0;
227        }
228    } else {
229        m2[..idx].fill(0);
230        let mut dst = m2[idx..].iter_mut();
231        for (a, b) in m.iter().zip(dst.by_ref()) {
232            *b = *a;
233        }
234        for b in dst {
235            *b = 0;
236        }
237    }
238}
239
240// Shift m right by n digits.
241pub fn shift_slice_right(m: &mut [Word], n: usize) {
242    let idx = n / WORD_BIT_SIZE;
243    let shift = n % WORD_BIT_SIZE;
244    if idx >= m.len() {
245        m.fill(0);
246    } else if shift > 0 {
247        let l = m.len();
248        let mut dst = m.as_mut_ptr();
249        unsafe {
250            let end = dst.add(l - 1);
251            let mut src = dst.add(idx);
252            loop {
253                if src < end {
254                    let mut d = *src >> shift;
255                    src = src.add(1);
256                    d |= *src << (WORD_BIT_SIZE - shift);
257                    *dst = d;
258                    dst = dst.add(1);
259                } else {
260                    break;
261                }
262            }
263            *dst = *src >> shift;
264        }
265        m[l - idx..].fill(0);
266    } else if idx > 0 {
267        let r = m.len() - idx;
268        m.copy_within(idx.., 0);
269        m[r..].fill(0);
270    }
271}
272
273pub fn count_leading_zeroes_skip_first(m: &[Word]) -> usize {
274    let mut iter = m.iter().rev();
275    let mut w;
276    let mut ret = 0;
277
278    if let Some(v) = iter.next() {
279        w = *v & (WORD_MAX >> 1);
280
281        while w == 0 {
282            ret += WORD_BIT_SIZE;
283
284            w = match iter.next() {
285                Some(v) => *v,
286                None => break,
287            }
288        }
289
290        if w != 0 {
291            while w & WORD_SIGNIFICANT_BIT == 0 {
292                w <<= 1;
293                ret += 1;
294            }
295        }
296    }
297
298    ret
299}
300
301pub fn count_leading_ones(m: &[Word]) -> usize {
302    let mut ret = 0;
303
304    for &v in m.iter().rev() {
305        if v == WORD_MAX {
306            ret += WORD_BIT_SIZE;
307        } else {
308            let mut v = v;
309
310            while v & WORD_SIGNIFICANT_BIT != 0 {
311                v <<= 1;
312                ret += 1;
313            }
314
315            break;
316        }
317    }
318
319    ret
320}
321
322/// Round precision to word bounday.
323pub fn round_p(p: usize) -> usize {
324    ((p.saturating_add(WORD_BIT_SIZE - 1)) / WORD_BIT_SIZE) * WORD_BIT_SIZE
325}
326
327// Convert rounding mode for an opposite sign.
328pub fn invert_rm_for_sign(rm: RoundingMode) -> RoundingMode {
329    if rm == RoundingMode::Up {
330        RoundingMode::Down
331    } else if rm == RoundingMode::Down {
332        RoundingMode::Up
333    } else {
334        rm
335    }
336}
337
338pub fn find_one_from(slice: &[Word], start_pos: usize) -> Option<usize> {
339    let start_idx = start_pos / WORD_BIT_SIZE;
340    if start_idx >= slice.len() {
341        None
342    } else {
343        let mut iter = slice.iter().rev().skip(start_idx);
344        if let Some(v) = iter.next() {
345            let mut d = *v;
346
347            let start_bit = start_pos % WORD_BIT_SIZE;
348            let mut shift = start_pos;
349
350            d <<= start_bit;
351
352            if d != 0 {
353                while d & WORD_SIGNIFICANT_BIT == 0 {
354                    d <<= 1;
355                    shift += 1;
356                }
357
358                return Some(shift);
359            }
360
361            shift += WORD_BIT_SIZE - start_bit;
362
363            for v in iter {
364                d = *v;
365
366                if d != 0 {
367                    while d & WORD_SIGNIFICANT_BIT == 0 {
368                        d <<= 1;
369                        shift += 1;
370                    }
371
372                    return Some(shift);
373                }
374
375                shift += WORD_BIT_SIZE;
376            }
377        }
378
379        None
380    }
381}
382
383/// Returns random subnormal number.
384#[cfg(test)]
385pub(crate) fn random_subnormal(p: usize) -> ExactNumNumber {
386    let p = round_p(if p < 3 * WORD_BIT_SIZE { 3 * WORD_BIT_SIZE } else { p });
387    let n = p - crate::common::test_rng::random::<usize>() % (2 * WORD_BIT_SIZE) - 1;
388    let mut m = Vec::with_capacity(p / WORD_BIT_SIZE);
389
390    for _ in 0..n / WORD_BIT_SIZE {
391        m.push(crate::common::test_rng::random::<Word>());
392    }
393
394    if n % WORD_BIT_SIZE > 0 {
395        let w = (crate::common::test_rng::random::<Word>() | WORD_SIGNIFICANT_BIT)
396            >> (WORD_BIT_SIZE - n % WORD_BIT_SIZE);
397        m.push(w);
398    } else {
399        *m.last_mut().unwrap() |= WORD_SIGNIFICANT_BIT;
400    }
401
402    m.resize(p / WORD_BIT_SIZE, 0);
403
404    let s = if crate::common::test_rng::random::<u8>() & 1 == 0 {
405        Sign::Pos
406    } else {
407        Sign::Neg
408    };
409
410    ExactNumNumber::from_raw_parts(&m, n, s, EXPONENT_MIN, false).unwrap()
411}
412
413#[cfg(test)]
414#[inline]
415pub fn rand_p() -> usize {
416    crate::common::test_rng::random::<usize>() % 1000 + crate::defs::DEFAULT_P
417}