Skip to content

Add a saturating_cast between integers #23596

Closed
@mahkoh

Description

@mahkoh

Similar to saturating_add but for casts. Untested implementation below.

use std;

pub trait IntExt<T> {
    fn saturating_cast(self) -> T;
}

// truncations that always preserve the value
macro_rules! trnc_up {
    ($src:ty as $($dst:ty),+) => {
        $(impl IntExt<$dst> for $src {
            fn saturating_cast(self) -> $dst {
                self as $dst
            }
        })+
    }
}

trnc_up!(u8 as u8, u16, i16, u32, i32, u64, i64, usize, isize);
trnc_up!(i8 as i8, i16, i32, i64, isize);
trnc_up!(u16 as u16, u32, i32, u64, i64, usize, isize);
trnc_up!(i16 as i16, i32, i64, isize);
trnc_up!(u32 as u32, u64, i64, usize);
trnc_up!(i32 as i32, i64, isize);
trnc_up!(u64 as u64);
trnc_up!(i64 as i64);
trnc_up!(usize as u64, usize);
trnc_up!(isize as i64, isize);
#[cfg(target_word_size = "64")]
trnc_up!(u64 as usize);
#[cfg(target_word_size = "64")]
trnc_up!(i64 as isize);
#[cfg(target_word_size = "32")]
trnc_up!(usize as u32);
#[cfg(target_word_size = "32")]
trnc_up!(isize as i32);

// truncations of signed tyes to unsigned types of equal or larger width
macro_rules! trnc_s2u_up {
    ($src:ty as $($dst:ty),+) => {
        $(impl IntExt<$dst> for $src {
            fn saturating_cast(self) -> $dst {
                if self >= 0 {
                    self as $dst
                } else {
                    0
                }
            }
        })+
    }
}

trnc_s2u_up!(i8 as u8, u16, u32, u64, usize);
trnc_s2u_up!(i16 as u16, u32, u64, usize);
trnc_s2u_up!(i32 as u32, u64, usize);
trnc_s2u_up!(i64 as u64);
trnc_s2u_up!(isize as usize);
#[cfg(target_word_size = "64")]
trnc_s2u_up!(i64 as usize);
#[cfg(target_word_size = "32")]
trnc_s2u_up!(isize as u32);

// truncation of signed types to types of smaller width
macro_rules! trnc_s_down {
    ($src:ty as $($dst:ident),+) => {
        $(impl IntExt<$dst> for $src {
            fn saturating_cast(self) -> $dst {
                if self > std::$dst::MAX as $src  {
                    std::$dst::MAX
                } else if self < std::$dst::MIN as $src {
                    std::$dst::MIN
                } else {
                    self as $dst
                }
            }
        })+
    }
}

trnc_s_down!(i16 as u8, i8);
trnc_s_down!(i32 as u8, i8, u16, i16);
trnc_s_down!(i64 as u8, i8, u16, i16, u32, i32);
trnc_s_down!(isize as u8, i8, u16, i16);
#[cfg(target_word_size = "64")]
trnc_s_down!(isize as u32, i32);
#[cfg(target_word_size = "32")]
trnc_s_down!(i64 as usize, isize);

// truncation of unsigned types to types of equal or smaller width
macro_rules! trnc_u_down {
    ($src:ty as $($dst:ident),+) => {
        $(impl IntExt<$dst> for $src {
            fn saturating_cast(self) -> $dst {
                if self > std::$dst::MAX as $src  {
                    std::$dst::MAX
                } else {
                    self as $dst
                }
            }
        })+
    }
}

trnc_u_down!(u16 as i8, u8, i16);
trnc_u_down!(u32 as i8, u8, i16, u16, i32);
trnc_u_down!(u64 as i8, u8, i16, u16, i32, u32, i64);
trnc_u_down!(usize as i8, u8, i16, u16, i32);
#[cfg(target_word_size = "64")]
trnc_u_down!(usize as u32, i64);
#[cfg(target_word_size = "32")]
trnc_u_down!(u64 as isize, usize);

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-enhancementCategory: An issue proposing an enhancement or a PR with one.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions