mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-07 22:04:10 -05:00
fix(core): fix decomposition algorithm not matching the theory
- problem arose from a shift being done on an unsigned value which did not keep the signed characteristics of the represented signed value - introduce an arithmetic_shift on the UnsignedInteger trait with a blanket implementation - add the edge case which revelead the issue - the asm has been verified to only change for the shift operation being applied, meaning no performance regression will occurr
This commit is contained in:
committed by
IceTDrinker
parent
ef07963767
commit
d75844dea5
@@ -143,9 +143,9 @@ pub(crate) fn decompose_one_level<S: UnsignedInteger>(
|
||||
mod_b_mask: S,
|
||||
) -> S {
|
||||
let res = *state & mod_b_mask;
|
||||
*state >>= base_log;
|
||||
*state = (*state).arithmetic_shr(base_log);
|
||||
let carry = decomposition_bit_trick(res, *state, base_log);
|
||||
*state += carry;
|
||||
*state = (*state).wrapping_add(carry);
|
||||
res.wrapping_sub(carry << base_log)
|
||||
}
|
||||
|
||||
|
||||
@@ -224,3 +224,17 @@ fn test_single_level_decompose_balanced() {
|
||||
// We expect an average value of 0 so the sum is also 0
|
||||
assert_eq!(sum, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decomposition_edge_case_sign_handling() {
|
||||
let decomposer = SignedDecomposer::new(DecompositionBaseLog(17), DecompositionLevelCount(3));
|
||||
let val: u64 = 0x8000_00e3_55b0_c827;
|
||||
|
||||
let decomp = decomposer.decompose(val);
|
||||
|
||||
let expected = [44422i64, 909, -65536];
|
||||
|
||||
for (term, expect) in decomp.zip(expected) {
|
||||
assert_eq!(term.value() as i64, expect, "Problem with term {term:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,12 @@ pub trait UnsignedInteger:
|
||||
/// Integer division rounding up.
|
||||
#[must_use]
|
||||
fn div_ceil(self, divisor: Self) -> Self;
|
||||
/// Interpret the value as signed and apply an arithmetic right shift, sign extending like on a
|
||||
/// signed value.
|
||||
#[must_use]
|
||||
fn arithmetic_shr(self, rhs: usize) -> Self {
|
||||
self.into_signed().shr(rhs).into_unsigned()
|
||||
}
|
||||
/// Return the casting of the current value to the signed type of the same size.
|
||||
fn into_signed(self) -> Self::Signed;
|
||||
/// Return a bit representation of the integer, where blocks of length `block_length` are
|
||||
|
||||
Reference in New Issue
Block a user