Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  rem.rs   Sprache: unbekannt

 
use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10};
use crate::decimal::{CalculationResult, Decimal};
use crate::ops::common::{Buf12, Buf16, Buf24, Dec64};

pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult {
    if d2.is_zero() {
        return CalculationResult::DivByZero;
    }
    if d1.is_zero() {
        return CalculationResult::Ok(Decimal::ZERO);
    }

    // We handle the structs a bit different here. Firstly, we ignore both the sign/scale of d2.
    // This is because during a remainder operation we do not care about the sign of the divisor
    // and only concern ourselves with that of the dividend.
    let mut d1 = Dec64::new(d1);
    let d2_scale = d2.scale();
    let mut d2 = Buf12::from_decimal(d2);

    let cmp = crate::ops::cmp::cmp_internal(
        &d1,
        &Dec64 {
            negative: d1.negative,
            scale: d2_scale,
            hi: d2.hi(),
            low64: d2.low64(),
        },
    );
    match cmp {
        core::cmp::Ordering::Equal => {
            // Same numbers meaning that remainder is zero
            return CalculationResult::Ok(Decimal::ZERO);
        }
        core::cmp::Ordering::Less => {
            // d1 < d2, e.g. 1/2. This means that the result is the value of d1
            return CalculationResult::Ok(d1.to_decimal());
        }
        core::cmp::Ordering::Greater => {}
    }

    // At this point we know that the dividend > divisor and that they are both non-zero.
    let mut scale = d1.scale as i32 - d2_scale as i32;
    if scale > 0 {
        // Scale up the divisor
        loop {
            let power = if scale >= MAX_I32_SCALE {
                POWERS_10[9]
            } else {
                POWERS_10[scale as usize]
            } as u64;

            let mut tmp = d2.lo() as u64 * power;
            d2.set_lo(tmp as u32);
            tmp >>= 32;
            tmp = tmp.wrapping_add((d2.mid() as u64 + ((d2.hi() as u64) << 32)) * power);
            d2.set_mid(tmp as u32);
            d2.set_hi((tmp >> 32) as u32);

            // Keep scaling if there is more to go
            scale -= MAX_I32_SCALE;
            if scale <= 0 {
                break;
            }
        }
        scale = 0;
    }

    loop {
        // If the dividend is smaller than the divisor then try to scale that up first
        if scale < 0 {
            let mut quotient = Buf12 {
                data: [d1.lo(), d1.mid(), d1.hi],
            };
            loop {
                // Figure out how much we can scale by
                let power_scale;
                if let Some(u) = quotient.find_scale(MAX_PRECISION_I32 + scale) {
                    if u >= POWERS_10.len() {
                        power_scale = 9;
                    } else {
                        power_scale = u;
                    }
                } else {
                    return CalculationResult::Overflow;
                };
                if power_scale == 0 {
                    break;
                }
                let power = POWERS_10[power_scale] as u64;
                scale += power_scale as i32;

                let mut tmp = quotient.data[0] as u64 * power;
                quotient.data[0] = tmp as u32;
                tmp >>= 32;
                quotient.set_high64(tmp.wrapping_add(quotient.high64().wrapping_mul(power)));
                if power_scale != 9 {
                    break;
                }
                if scale >= 0 {
                    break;
                }
            }
            d1.low64 = quotient.low64();
            d1.hi = quotient.data[2];
            d1.scale = d2_scale;
        }

        // if the high portion is empty then return the modulus of the bottom portion
        if d1.hi == 0 {
            d1.low64 %= d2.low64();
            return CalculationResult::Ok(d1.to_decimal());
        } else if (d2.mid() | d2.hi()) == 0 {
            let mut tmp = d1.high64();
            tmp = ((tmp % d2.lo() as u64) << 32) | (d1.lo() as u64);
            d1.low64 = tmp % d2.lo() as u64;
            d1.hi = 0;
        } else {
            // Divisor is > 32 bits
            return rem_full(&d1, &d2, scale);
        }

        if scale >= 0 {
            break;
        }
    }

    CalculationResult::Ok(d1.to_decimal())
}

fn rem_full(d1: &Dec64, d2: &Buf12, scale: i32) -> CalculationResult {
    let mut scale = scale;

    // First normalize the divisor
    let shift = if d2.hi() == 0 {
        d2.mid().leading_zeros()
    } else {
        d2.hi().leading_zeros()
    };

    let mut buffer = Buf24::zero();
    let mut overflow = 0u32;
    buffer.set_low64(d1.low64 << shift);
    buffer.set_mid64(((d1.mid() as u64).wrapping_add((d1.hi as u64) << 32)) >> (32 - shift));
    let mut upper = 3; // We start at 3 due to bit shifting

    while scale < 0 {
        let power = if -scale >= MAX_I32_SCALE {
            POWERS_10[9]
        } else {
            POWERS_10[-scale as usize]
        } as u64;
        let mut tmp64 = buffer.data[0] as u64 * power;
        buffer.data[0] = tmp64 as u32;

        for (index, part) in buffer.data.iter_mut().enumerate().skip(1) {
            if index > upper {
                break;
            }
            tmp64 >>= 32;
            tmp64 = tmp64.wrapping_add((*part as u64).wrapping_mul(power));
            *part = tmp64 as u32;
        }
        // If we have overflow then also process that
        if upper == 6 {
            tmp64 >>= 32;
            tmp64 = tmp64.wrapping_add((overflow as u64).wrapping_mul(power));
            overflow = tmp64 as u32;
        }

        // Make sure the high bit is not set
        if tmp64 > 0x7FFF_FFFF {
            upper += 1;
            if upper > 5 {
                overflow = (tmp64 >> 32) as u32;
            } else {
                buffer.data[upper] = (tmp64 >> 32) as u32;
            }
        }
        scale += MAX_I32_SCALE;
    }

    // TODO: Optimize slice logic

    let mut tmp = Buf16::zero();
    let divisor = d2.low64() << shift;
    if d2.hi() == 0 {
        // Do some division
        if upper == 6 {
            upper -= 1;

            tmp.data = [buffer.data[4], buffer.data[5], overflow, 0];
            tmp.partial_divide_64(divisor);
            buffer.data[4] = tmp.data[0];
            buffer.data[5] = tmp.data[1];
        }
        if upper == 5 {
            upper -= 1;
            tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], 0];
            tmp.partial_divide_64(divisor);
            buffer.data[3] = tmp.data[0];
            buffer.data[4] = tmp.data[1];
            buffer.data[5] = tmp.data[2];
        }
        if upper == 4 {
            tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], 0];
            tmp.partial_divide_64(divisor);
            buffer.data[2] = tmp.data[0];
            buffer.data[3] = tmp.data[1];
            buffer.data[4] = tmp.data[2];
        }

        tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], 0];
        tmp.partial_divide_64(divisor);
        buffer.data[1] = tmp.data[0];
        buffer.data[2] = tmp.data[1];
        buffer.data[3] = tmp.data[2];

        tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], 0];
        tmp.partial_divide_64(divisor);
        buffer.data[0] = tmp.data[0];
        buffer.data[1] = tmp.data[1];
        buffer.data[2] = tmp.data[2];

        let low64 = buffer.low64() >> shift;
        CalculationResult::Ok(Decimal::from_parts(
            low64 as u32,
            (low64 >> 32) as u32,
            0,
            d1.negative,
            d1.scale,
        ))
    } else {
        let divisor_low64 = divisor;
        let divisor = Buf12 {
            data: [
                divisor_low64 as u32,
                (divisor_low64 >> 32) as u32,
                (((d2.mid() as u64) + ((d2.hi() as u64) << 32)) >> (32 - shift)) as u32,
            ],
        };

        // Do some division
        if upper == 6 {
            upper -= 1;
            tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], overflow];
            tmp.partial_divide_96(&divisor);
            buffer.data[3] = tmp.data[0];
            buffer.data[4] = tmp.data[1];
            buffer.data[5] = tmp.data[2];
        }
        if upper == 5 {
            upper -= 1;
            tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], buffer.data[5]];
            tmp.partial_divide_96(&divisor);
            buffer.data[2] = tmp.data[0];
            buffer.data[3] = tmp.data[1];
            buffer.data[4] = tmp.data[2];
            buffer.data[5] = tmp.data[3];
        }
        if upper == 4 {
            tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], buffer.data[4]];
            tmp.partial_divide_96(&divisor);
            buffer.data[1] = tmp.data[0];
            buffer.data[2] = tmp.data[1];
            buffer.data[3] = tmp.data[2];
            buffer.data[4] = tmp.data[3];
        }

        tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], buffer.data[3]];
        tmp.partial_divide_96(&divisor);
        buffer.data[0] = tmp.data[0];
        buffer.data[1] = tmp.data[1];
        buffer.data[2] = tmp.data[2];
        buffer.data[3] = tmp.data[3];

        let low64 = (buffer.low64() >> shift) + ((buffer.data[2] as u64) << (32 - shift) << 32);
        CalculationResult::Ok(Decimal::from_parts(
            low64 as u32,
            (low64 >> 32) as u32,
            buffer.data[2] >> shift,
            d1.negative,
            d1.scale,
        ))
    }
}

[ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge