ToUnsigned trait


#1

Hello!

I found no methods in stdlib to convert signed to unsigned so that signed::MIN converts to unsigned::MIN and respectively signed::MAX to unsigned::MAX so I wrote this trait:

use std::{u8, u16, u32, u64, usize};

pub trait ToUnsigned<A> {
    fn to_unsigned (&self) -> A;
}

impl ToUnsigned<u8> for i8 {
    fn to_unsigned (&self) -> u8 {
        *self as u8 ^ (u8::MAX / 2 + 1)
    }
}

impl ToUnsigned<u16> for i16 {
    fn to_unsigned (&self) -> u16 {
        *self as u16 ^ (u16::MAX / 2 + 1)
    }
}

impl ToUnsigned<u32> for i32 {
    fn to_unsigned (&self) -> u32 {
        *self as u32 ^ (u32::MAX / 2 + 1)
    }
}

impl ToUnsigned<u64> for i64 {
    fn to_unsigned (&self) -> u64 {
        *self as u64 ^ (u64::MAX / 2 + 1)
    }
}

impl ToUnsigned<usize> for isize {
    fn to_unsigned (&self) -> usize {
        *self as usize ^ (usize::MAX / 2 + 1)
    }
}

It would be great to hear your judgment. Any comments suggestions on how to improve, rewrite more right are welcome. Thank you.


#2

Shifting the entire range seems like a mathematically odd thing to do. You preserve ordering, I guess, but are the values still meaningful otherwise? What do you use this for?

If this is only meant for primitives, I think it’s more idiomatic to let them Copy as self rather than taking &self. I’m not sure what other types you might apply it to – the semantics are impossible for unbounded BigInt, for instance.


#3

I use this for converting heightmaps. I have a heightmaps with signed heights (z coordinates) as input and need unsigned at output. I believe this kind of convertion could also be used to convert PCM audio samples from/to signed/unsigned formats.
I’m not sure about ordering. Most probably this trick will not work with big endian.

Yes, this is for primitives at the moment. This definitely should be ‘self’ and not ‘&self’.
Looks like, this convertion implementation is quick, but not portable. There are also “branching” implementation and “convert to higher sized int” implementation, but both with their own drawbacks. Not so simple task as I thought.

Thank you for your suggestions!


#4

I would choose a different name. To me, ToUnsigned implies that it’s just typecasting the value, some_i64 as u64 and nothing more. Since you’re basically shifting the number line, maybe put that in the name like ShiftToUnsigned so the intent is clear. The final name is up to you, of course – just think about readability.

It should be fine, since you’re toggling the MSB numerically. That only makes an assumption about having a 2’s complement representation, but I think that’s safe. Endianness issues come up more when you’re working on raw bytes – then the arrangement of those bytes would matter.


#5

I agree, ShiftToUnsigned is more suitable. So… renamed! Thank you!


#6

This looks similar to a trait I have planned for my aurum-numeric crate but haven’t implemented yet. I plan a trait called BiclampCast which interpolates between machine scalars. It would have this effect when converting between signed and unsigned types, but would also interpolate to f32 and f64 to values between -1.0 and 1.0. For example, converting from -1.0 to an i8 would result in -128, or u8 would result in 0. The closest thing I have so far is ClampCast which excludes negative values. I’m using ClampCast for conversion between color types while I plan to use BiclampCast for converting audio samples.


#7

Cool! Feel free to use this as a sketch for BiclampCast. I did tests for this trait today. I could use your crate after you publish it.