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


Quelle  legacy.rs   Sprache: unbekannt

 
use crate::{
    constants::{MAX_PRECISION_U32, POWERS_10, U32_MASK},
    decimal::{CalculationResult, Decimal},
    ops::array::{
        add_by_internal, cmp_internal, div_by_u32, is_all_zero, mul_by_u32, mul_part, rescale_internal, shl1_internal,
    },
};

use core::cmp::Ordering;
use num_traits::Zero;

pub(crate) fn add_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult {
    // Convert to the same scale
    let mut my = d1.mantissa_array3();
    let mut my_scale = d1.scale();
    let mut ot = d2.mantissa_array3();
    let mut other_scale = d2.scale();
    rescale_to_maximum_scale(&mut my, &mut my_scale, &mut ot, &mut other_scale);
    let mut final_scale = my_scale.max(other_scale);

    // Add the items together
    let my_negative = d1.is_sign_negative();
    let other_negative = d2.is_sign_negative();
    let mut negative = false;
    let carry;
    if !(my_negative ^ other_negative) {
        negative = my_negative;
        carry = add_by_internal3(&mut my, &ot);
    } else {
        let cmp = cmp_internal(&my, &ot);
        // -x + y
        // if x > y then it's negative (i.e. -2 + 1)
        match cmp {
            Ordering::Less => {
                negative = other_negative;
                sub_by_internal3(&mut ot, &my);
                my[0] = ot[0];
                my[1] = ot[1];
                my[2] = ot[2];
            }
            Ordering::Greater => {
                negative = my_negative;
                sub_by_internal3(&mut my, &ot);
            }
            Ordering::Equal => {
                // -2 + 2
                my[0] = 0;
                my[1] = 0;
                my[2] = 0;
            }
        }
        carry = 0;
    }

    // If we have a carry we underflowed.
    // We need to lose some significant digits (if possible)
    if carry > 0 {
        if final_scale == 0 {
            return CalculationResult::Overflow;
        }

        // Copy it over to a temp array for modification
        let mut temp = [my[0], my[1], my[2], carry];
        while final_scale > 0 && temp[3] != 0 {
            div_by_u32(&mut temp, 10);
            final_scale -= 1;
        }

        // If we still have a carry bit then we overflowed
        if temp[3] > 0 {
            return CalculationResult::Overflow;
        }

        // Copy it back - we're done
        my[0] = temp[0];
        my[1] = temp[1];
        my[2] = temp[2];
    }

    CalculationResult::Ok(Decimal::from_parts(my[0], my[1], my[2], negative, final_scale))
}

pub(crate) fn sub_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult {
    add_impl(d1, &(-*d2))
}

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

    let dividend = d1.mantissa_array3();
    let divisor = d2.mantissa_array3();
    let mut quotient = [0u32, 0u32, 0u32];
    let mut quotient_scale: i32 = d1.scale() as i32 - d2.scale() as i32;

    // We supply an extra overflow word for each of the dividend and the remainder
    let mut working_quotient = [dividend[0], dividend[1], dividend[2], 0u32];
    let mut working_remainder = [0u32, 0u32, 0u32, 0u32];
    let mut working_scale = quotient_scale;
    let mut remainder_scale = quotient_scale;
    let mut underflow;

    loop {
        div_internal(&mut working_quotient, &mut working_remainder, &divisor);
        underflow = add_with_scale_internal(
            &mut quotient,
            &mut quotient_scale,
            &mut working_quotient,
            &mut working_scale,
        );

        // Multiply the remainder by 10
        let mut overflow = 0;
        for part in working_remainder.iter_mut() {
            let (lo, hi) = mul_part(*part, 10, overflow);
            *part = lo;
            overflow = hi;
        }
        // Copy temp remainder into the temp quotient section
        working_quotient.copy_from_slice(&working_remainder);

        remainder_scale += 1;
        working_scale = remainder_scale;

        if underflow || is_all_zero(&working_remainder) {
            break;
        }
    }

    // If we have a really big number try to adjust the scale to 0
    while quotient_scale < 0 {
        copy_array_diff_lengths(&mut working_quotient, "ient);
        working_quotient[3] = 0;
        working_remainder.iter_mut().for_each(|x| *x = 0);

        // Mul 10
        let mut overflow = 0;
        for part in &mut working_quotient {
            let (lo, hi) = mul_part(*part, 10, overflow);
            *part = lo;
            overflow = hi;
        }
        for part in &mut working_remainder {
            let (lo, hi) = mul_part(*part, 10, overflow);
            *part = lo;
            overflow = hi;
        }
        if working_quotient[3] == 0 && is_all_zero(&working_remainder) {
            quotient_scale += 1;
            quotient[0] = working_quotient[0];
            quotient[1] = working_quotient[1];
            quotient[2] = working_quotient[2];
        } else {
            // Overflow
            return CalculationResult::Overflow;
        }
    }

    if quotient_scale > 255 {
        quotient[0] = 0;
        quotient[1] = 0;
        quotient[2] = 0;
        quotient_scale = 0;
    }

    let mut quotient_negative = d1.is_sign_negative() ^ d2.is_sign_negative();

    // Check for underflow
    let mut final_scale: u32 = quotient_scale as u32;
    if final_scale > MAX_PRECISION_U32 {
        let mut remainder = 0;

        // Division underflowed. We must remove some significant digits over using
        //  an invalid scale.
        while final_scale > MAX_PRECISION_U32 && !is_all_zero("ient) {
            remainder = div_by_u32(&mut quotient, 10);
            final_scale -= 1;
        }
        if final_scale > MAX_PRECISION_U32 {
            // Result underflowed so set to zero
            final_scale = 0;
            quotient_negative = false;
        } else if remainder >= 5 {
            for part in &mut quotient {
                if remainder == 0 {
                    break;
                }
                let digit: u64 = u64::from(*part) + 1;
                remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 };
                *part = (digit & 0xFFFF_FFFF) as u32;
            }
        }
    }

    CalculationResult::Ok(Decimal::from_parts(
        quotient[0],
        quotient[1],
        quotient[2],
        quotient_negative,
        final_scale,
    ))
}

pub(crate) fn mul_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult {
    // Early exit if either is zero
    if d1.is_zero() || d2.is_zero() {
        return CalculationResult::Ok(Decimal::zero());
    }

    // We are only resulting in a negative if we have mismatched signs
    let negative = d1.is_sign_negative() ^ d2.is_sign_negative();

    // We get the scale of the result by adding the operands. This may be too big, however
    //  we'll correct later
    let mut final_scale = d1.scale() + d2.scale();

    // First of all, if ONLY the lo parts of both numbers is filled
    // then we can simply do a standard 64 bit calculation. It's a minor
    // optimization however prevents the need for long form multiplication
    let my = d1.mantissa_array3();
    let ot = d2.mantissa_array3();
    if my[1] == 0 && my[2] == 0 && ot[1] == 0 && ot[2] == 0 {
        // Simply multiplication
        let mut u64_result = u64_to_array(u64::from(my[0]) * u64::from(ot[0]));

        // If we're above max precision then this is a very small number
        if final_scale > MAX_PRECISION_U32 {
            final_scale -= MAX_PRECISION_U32;

            // If the number is above 19 then this will equate to zero.
            // This is because the max value in 64 bits is 1.84E19
            if final_scale > 19 {
                return CalculationResult::Ok(Decimal::zero());
            }

            let mut rem_lo = 0;
            let mut power;
            if final_scale > 9 {
                // Since 10^10 doesn't fit into u32, we divide by 10^10/4
                // and multiply the next divisor by 4.
                rem_lo = div_by_u32(&mut u64_result, 2_500_000_000);
                power = POWERS_10[final_scale as usize - 10] << 2;
            } else {
                power = POWERS_10[final_scale as usize];
            }

            // Divide fits in 32 bits
            let rem_hi = div_by_u32(&mut u64_result, power);

            // Round the result. Since the divisor is a power of 10
            // we check to see if the remainder is >= 1/2 divisor
            power >>= 1;
            if rem_hi >= power && (rem_hi > power || (rem_lo | (u64_result[0] & 0x1)) != 0) {
                u64_result[0] += 1;
            }

            final_scale = MAX_PRECISION_U32;
        }
        return CalculationResult::Ok(Decimal::from_parts(
            u64_result[0],
            u64_result[1],
            0,
            negative,
            final_scale,
        ));
    }

    // We're using some of the high bits, so we essentially perform
    // long form multiplication. We compute the 9 partial products
    // into a 192 bit result array.
    //
    //                     [my-h][my-m][my-l]
    //                  x  [ot-h][ot-m][ot-l]
    // --------------------------------------
    // 1.                        [r-hi][r-lo] my-l * ot-l [0, 0]
    // 2.                  [r-hi][r-lo]       my-l * ot-m [0, 1]
    // 3.                  [r-hi][r-lo]       my-m * ot-l [1, 0]
    // 4.            [r-hi][r-lo]             my-m * ot-m [1, 1]
    // 5.            [r-hi][r-lo]             my-l * ot-h [0, 2]
    // 6.            [r-hi][r-lo]             my-h * ot-l [2, 0]
    // 7.      [r-hi][r-lo]                   my-m * ot-h [1, 2]
    // 8.      [r-hi][r-lo]                   my-h * ot-m [2, 1]
    // 9.[r-hi][r-lo]                         my-h * ot-h [2, 2]
    let mut product = [0u32, 0u32, 0u32, 0u32, 0u32, 0u32];

    // We can perform a minor short circuit here. If the
    // high portions are both 0 then we can skip portions 5-9
    let to = if my[2] == 0 && ot[2] == 0 { 2 } else { 3 };

    for (my_index, my_item) in my.iter().enumerate().take(to) {
        for (ot_index, ot_item) in ot.iter().enumerate().take(to) {
            let (mut rlo, mut rhi) = mul_part(*my_item, *ot_item, 0);

            // Get the index for the lo portion of the product
            for prod in product.iter_mut().skip(my_index + ot_index) {
                let (res, overflow) = add_part(rlo, *prod);
                *prod = res;

                // If we have something in rhi from before then promote that
                if rhi > 0 {
                    // If we overflowed in the last add, add that with rhi
                    if overflow > 0 {
                        let (nlo, nhi) = add_part(rhi, overflow);
                        rlo = nlo;
                        rhi = nhi;
                    } else {
                        rlo = rhi;
                        rhi = 0;
                    }
                } else if overflow > 0 {
                    rlo = overflow;
                    rhi = 0;
                } else {
                    break;
                }

                // If nothing to do next round then break out
                if rlo == 0 {
                    break;
                }
            }
        }
    }

    // If our result has used up the high portion of the product
    // then we either have an overflow or an underflow situation
    // Overflow will occur if we can't scale it back, whereas underflow
    // with kick in rounding
    let mut remainder = 0;
    while final_scale > 0 && (product[3] != 0 || product[4] != 0 || product[5] != 0) {
        remainder = div_by_u32(&mut product, 10u32);
        final_scale -= 1;
    }

    // Round up the carry if we need to
    if remainder >= 5 {
        for part in product.iter_mut() {
            if remainder == 0 {
                break;
            }
            let digit: u64 = u64::from(*part) + 1;
            remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 };
            *part = (digit & 0xFFFF_FFFF) as u32;
        }
    }

    // If we're still above max precision then we'll try again to
    // reduce precision - we may be dealing with a limit of "0"
    if final_scale > MAX_PRECISION_U32 {
        // We're in an underflow situation
        // The easiest way to remove precision is to divide off the result
        while final_scale > MAX_PRECISION_U32 && !is_all_zero(&product) {
            div_by_u32(&mut product, 10);
            final_scale -= 1;
        }
        // If we're still at limit then we can't represent any
        // significant decimal digits and will return an integer only
        // Can also be invoked while representing 0.
        if final_scale > MAX_PRECISION_U32 {
            final_scale = 0;
        }
    } else if !(product[3] == 0 && product[4] == 0 && product[5] == 0) {
        // We're in an overflow situation - we're within our precision bounds
        // but still have bits in overflow
        return CalculationResult::Overflow;
    }

    CalculationResult::Ok(Decimal::from_parts(
        product[0],
        product[1],
        product[2],
        negative,
        final_scale,
    ))
}

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());
    }

    // Rescale so comparable
    let initial_scale = d1.scale();
    let mut quotient = d1.mantissa_array3();
    let mut quotient_scale = initial_scale;
    let mut divisor = d2.mantissa_array3();
    let mut divisor_scale = d2.scale();
    rescale_to_maximum_scale(&mut quotient, &mut quotient_scale, &mut divisor, &mut divisor_scale);

    // Working is the remainder + the quotient
    // We use an aligned array since we'll be using it a lot.
    let mut working_quotient = [quotient[0], quotient[1], quotient[2], 0u32];
    let mut working_remainder = [0u32, 0u32, 0u32, 0u32];
    div_internal(&mut working_quotient, &mut working_remainder, &divisor);

    // Round if necessary. This is for semantic correctness, but could feasibly be removed for
    // performance improvements.
    if quotient_scale > initial_scale {
        let mut working = [
            working_remainder[0],
            working_remainder[1],
            working_remainder[2],
            working_remainder[3],
        ];
        while quotient_scale > initial_scale {
            if div_by_u32(&mut working, 10) > 0 {
                break;
            }
            quotient_scale -= 1;
            working_remainder.copy_from_slice(&working);
        }
    }

    CalculationResult::Ok(Decimal::from_parts(
        working_remainder[0],
        working_remainder[1],
        working_remainder[2],
        d1.is_sign_negative(),
        quotient_scale,
    ))
}

pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering {
    // Quick exit if major differences
    if d1.is_zero() && d2.is_zero() {
        return Ordering::Equal;
    }
    let self_negative = d1.is_sign_negative();
    let other_negative = d2.is_sign_negative();
    if self_negative && !other_negative {
        return Ordering::Less;
    } else if !self_negative && other_negative {
        return Ordering::Greater;
    }

    // If we have 1.23 and 1.2345 then we have
    //  123 scale 2 and 12345 scale 4
    //  We need to convert the first to
    //  12300 scale 4 so we can compare equally
    let left: &Decimal;
    let right: &Decimal;
    if self_negative && other_negative {
        // Both are negative, so reverse cmp
        left = d2;
        right = d1;
    } else {
        left = d1;
        right = d2;
    }
    let mut left_scale = left.scale();
    let mut right_scale = right.scale();
    let mut left_raw = left.mantissa_array3();
    let mut right_raw = right.mantissa_array3();

    if left_scale == right_scale {
        // Fast path for same scale
        if left_raw[2] != right_raw[2] {
            return left_raw[2].cmp(&right_raw[2]);
        }
        if left_raw[1] != right_raw[1] {
            return left_raw[1].cmp(&right_raw[1]);
        }
        return left_raw[0].cmp(&right_raw[0]);
    }

    // Rescale and compare
    rescale_to_maximum_scale(&mut left_raw, &mut left_scale, &mut right_raw, &mut right_scale);
    cmp_internal(&left_raw, &right_raw)
}

#[inline]
fn add_part(left: u32, right: u32) -> (u32, u32) {
    let added = u64::from(left) + u64::from(right);
    ((added & U32_MASK) as u32, (added >> 32 & U32_MASK) as u32)
}

#[inline(always)]
fn sub_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) {
    let mut overflow = 0;
    let vl = value.len();
    for i in 0..vl {
        let part = (0x1_0000_0000u64 + u64::from(value[i])) - (u64::from(by[i]) + overflow);
        value[i] = part as u32;
        overflow = 1 - (part >> 32);
    }
}

fn div_internal(quotient: &mut [u32; 4], remainder: &mut [u32; 4], divisor: &[u32; 3]) {
    // There are a couple of ways to do division on binary numbers:
    //   1. Using long division
    //   2. Using the complement method
    // ref: http://paulmason.me/dividing-binary-numbers-part-2/
    // The complement method basically keeps trying to subtract the
    // divisor until it can't anymore and placing the rest in remainder.
    let mut complement = [
        divisor[0] ^ 0xFFFF_FFFF,
        divisor[1] ^ 0xFFFF_FFFF,
        divisor[2] ^ 0xFFFF_FFFF,
        0xFFFF_FFFF,
    ];

    // Add one onto the complement
    add_one_internal4(&mut complement);

    // Make sure the remainder is 0
    remainder.iter_mut().for_each(|x| *x = 0);

    // If we have nothing in our hi+ block then shift over till we do
    let mut blocks_to_process = 0;
    while blocks_to_process < 4 && quotient[3] == 0 {
        // memcpy would be useful here
        quotient[3] = quotient[2];
        quotient[2] = quotient[1];
        quotient[1] = quotient[0];
        quotient[0] = 0;

        // Increment the counter
        blocks_to_process += 1;
    }

    // Let's try and do the addition...
    let mut block = blocks_to_process << 5;
    let mut working = [0u32, 0u32, 0u32, 0u32];
    while block < 128 {
        // << 1 for quotient AND remainder. Moving the carry from the quotient to the bottom of the
        // remainder.
        let carry = shl1_internal(quotient, 0);
        shl1_internal(remainder, carry);

        // Copy the remainder of working into sub
        working.copy_from_slice(remainder);

        // Add the remainder with the complement
        add_by_internal(&mut working, &complement);

        // Check for the significant bit - move over to the quotient
        // as necessary
        if (working[3] & 0x8000_0000) == 0 {
            remainder.copy_from_slice(&working);
            quotient[0] |= 1;
        }

        // Increment our pointer
        block += 1;
    }
}

#[inline]
fn copy_array_diff_lengths(into: &mut [u32], from: &[u32]) {
    for i in 0..into.len() {
        if i >= from.len() {
            break;
        }
        into[i] = from[i];
    }
}

#[inline]
fn add_one_internal4(value: &mut [u32; 4]) -> u32 {
    let mut carry: u64 = 1; // Start with one, since adding one
    let mut sum: u64;
    for i in value.iter_mut() {
        sum = (*i as u64) + carry;
        *i = (sum & U32_MASK) as u32;
        carry = sum >> 32;
    }

    carry as u32
}

#[inline]
fn add_by_internal3(value: &mut [u32; 3], by: &[u32; 3]) -> u32 {
    let mut carry: u32 = 0;
    let bl = by.len();
    for i in 0..bl {
        let res1 = value[i].overflowing_add(by[i]);
        let res2 = res1.0.overflowing_add(carry);
        value[i] = res2.0;
        carry = (res1.1 | res2.1) as u32;
    }
    carry
}

#[inline]
const fn u64_to_array(value: u64) -> [u32; 2] {
    [(value & U32_MASK) as u32, (value >> 32 & U32_MASK) as u32]
}

fn add_with_scale_internal(
    quotient: &mut [u32; 3],
    quotient_scale: &mut i32,
    working_quotient: &mut [u32; 4],
    working_scale: &mut i32,
) -> bool {
    // Add quotient and the working (i.e. quotient = quotient + working)
    if is_all_zero(quotient) {
        // Quotient is zero so we can just copy the working quotient in directly
        // First, make sure they are both 96 bit.
        while working_quotient[3] != 0 {
            div_by_u32(working_quotient, 10);
            *working_scale -= 1;
        }
        copy_array_diff_lengths(quotient, working_quotient);
        *quotient_scale = *working_scale;
        return false;
    }

    if is_all_zero(working_quotient) {
        return false;
    }

    // We have ensured that our working is not zero so we should do the addition

    // If our two quotients are different then
    // try to scale down the one with the bigger scale
    let mut temp3 = [0u32, 0u32, 0u32];
    let mut temp4 = [0u32, 0u32, 0u32, 0u32];
    if *quotient_scale != *working_scale {
        // TODO: Remove necessity for temp (without performance impact)
        fn div_by_10<const N: usize>(target: &mut [u32], temp: &mut [u32; N], scale: &mut i32, target_scale: i32) {
            // Copy to the temp array
            temp.copy_from_slice(target);
            // divide by 10 until target scale is reached
            while *scale > target_scale {
                let remainder = div_by_u32(temp, 10);
                if remainder == 0 {
                    *scale -= 1;
                    target.copy_from_slice(temp);
                } else {
                    break;
                }
            }
        }

        if *quotient_scale < *working_scale {
            div_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale);
        } else {
            div_by_10(quotient, &mut temp3, quotient_scale, *working_scale);
        }
    }

    // If our two quotients are still different then
    // try to scale up the smaller scale
    if *quotient_scale != *working_scale {
        // TODO: Remove necessity for temp (without performance impact)
        fn mul_by_10(target: &mut [u32], temp: &mut [u32], scale: &mut i32, target_scale: i32) {
            temp.copy_from_slice(target);
            let mut overflow = 0;
            // Multiply by 10 until target scale reached or overflow
            while *scale < target_scale && overflow == 0 {
                overflow = mul_by_u32(temp, 10);
                if overflow == 0 {
                    // Still no overflow
                    *scale += 1;
                    target.copy_from_slice(temp);
                }
            }
        }

        if *quotient_scale > *working_scale {
            mul_by_10(working_quotient, &mut temp4, working_scale, *quotient_scale);
        } else {
            mul_by_10(quotient, &mut temp3, quotient_scale, *working_scale);
        }
    }

    // If our two quotients are still different then
    // try to scale down the one with the bigger scale
    // (ultimately losing significant digits)
    if *quotient_scale != *working_scale {
        // TODO: Remove necessity for temp (without performance impact)
        fn div_by_10_lossy<const N: usize>(
            target: &mut [u32],
            temp: &mut [u32; N],
            scale: &mut i32,
            target_scale: i32,
        ) {
            temp.copy_from_slice(target);
            // divide by 10 until target scale is reached
            while *scale > target_scale {
                div_by_u32(temp, 10);
                *scale -= 1;
                target.copy_from_slice(temp);
            }
        }
        if *quotient_scale < *working_scale {
            div_by_10_lossy(working_quotient, &mut temp4, working_scale, *quotient_scale);
        } else {
            div_by_10_lossy(quotient, &mut temp3, quotient_scale, *working_scale);
        }
    }

    // If quotient or working are zero we have an underflow condition
    if is_all_zero(quotient) || is_all_zero(working_quotient) {
        // Underflow
        return true;
    } else {
        // Both numbers have the same scale and can be added.
        // We just need to know whether we can fit them in
        let mut underflow = false;
        let mut temp = [0u32, 0u32, 0u32];
        while !underflow {
            temp.copy_from_slice(quotient);

            // Add the working quotient
            let overflow = add_by_internal(&mut temp, working_quotient);
            if overflow == 0 {
                // addition was successful
                quotient.copy_from_slice(&temp);
                break;
            } else {
                // addition overflowed - remove significant digits and try again
                div_by_u32(quotient, 10);
                *quotient_scale -= 1;
                div_by_u32(working_quotient, 10);
                *working_scale -= 1;
                // Check for underflow
                underflow = is_all_zero(quotient) || is_all_zero(working_quotient);
            }
        }
        if underflow {
            return true;
        }
    }
    false
}

/// Rescales the given decimals to equivalent scales.
/// It will firstly try to scale both the left and the right side to
/// the maximum scale of left/right. If it is unable to do that it
/// will try to reduce the accuracy of the other argument.
/// e.g. with 1.23 and 2.345 it'll rescale the first arg to 1.230
#[inline(always)]
fn rescale_to_maximum_scale(left: &mut [u32; 3], left_scale: &mut u32, right: &mut [u32; 3], right_scale: &mut u32) {
    if left_scale == right_scale {
        // Nothing to do
        return;
    }

    if is_all_zero(left) {
        *left_scale = *right_scale;
        return;
    } else if is_all_zero(right) {
        *right_scale = *left_scale;
        return;
    }

    if left_scale > right_scale {
        rescale_internal(right, right_scale, *left_scale);
        if right_scale != left_scale {
            rescale_internal(left, left_scale, *right_scale);
        }
    } else {
        rescale_internal(left, left_scale, *right_scale);
        if right_scale != left_scale {
            rescale_internal(right, right_scale, *left_scale);
        }
    }
}

#[cfg(test)]
mod test {
    // Tests on private methods.
    //
    // All public tests should go under `tests/`.

    use super::*;
    use crate::prelude::*;

    #[test]
    fn it_can_rescale_to_maximum_scale() {
        fn extract(value: &str) -> ([u32; 3], u32) {
            let v = Decimal::from_str(value).unwrap();
            (v.mantissa_array3(), v.scale())
        }

        let tests = &[
            ("1", "1", "1", "1"),
            ("1", "1.0", "1.0", "1.0"),
            ("1", "1.00000", "1.00000", "1.00000"),
            ("1", "1.0000000000", "1.0000000000", "1.0000000000"),
            (
                "1",
                "1.00000000000000000000",
                "1.00000000000000000000",
                "1.00000000000000000000",
            ),
            ("1.1", "1.1", "1.1", "1.1"),
            ("1.1", "1.10000", "1.10000", "1.10000"),
            ("1.1", "1.1000000000", "1.1000000000", "1.1000000000"),
            (
                "1.1",
                "1.10000000000000000000",
                "1.10000000000000000000",
                "1.10000000000000000000",
            ),
            (
                "0.6386554621848739495798319328",
                "11.815126050420168067226890757",
                "0.638655462184873949579831933",
                "11.815126050420168067226890757",
            ),
            (
                "0.0872727272727272727272727272", // Scale 28
                "843.65000000",                   // Scale 8
                "0.0872727272727272727272727",    // 25
                "843.6500000000000000000000000",  // 25
            ),
        ];

        for &(left_raw, right_raw, expected_left, expected_right) in tests {
            // Left = the value to rescale
            // Right = the new scale we're scaling to
            // Expected = the expected left value after rescale
            let (expected_left, expected_lscale) = extract(expected_left);
            let (expected_right, expected_rscale) = extract(expected_right);

            let (mut left, mut left_scale) = extract(left_raw);
            let (mut right, mut right_scale) = extract(right_raw);
            rescale_to_maximum_scale(&mut left, &mut left_scale, &mut right, &mut right_scale);
            assert_eq!(left, expected_left);
            assert_eq!(left_scale, expected_lscale);
            assert_eq!(right, expected_right);
            assert_eq!(right_scale, expected_rscale);

            // Also test the transitive case
            let (mut left, mut left_scale) = extract(left_raw);
            let (mut right, mut right_scale) = extract(right_raw);
            rescale_to_maximum_scale(&mut right, &mut right_scale, &mut left, &mut left_scale);
            assert_eq!(left, expected_left);
            assert_eq!(left_scale, expected_lscale);
            assert_eq!(right, expected_right);
            assert_eq!(right_scale, expected_rscale);
        }
    }
}

[ Dauer der Verarbeitung: 0.36 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