#![deny(missing_docs)]
#![deny(warnings)]
#![allow(const_err)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(all(feature = "x128", not(stable_i128)), feature(i128_type, i128))]
#[cfg(feature = "std")]
extern crate core;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
use core::fmt;
#[cfg(feature = "std")]
use std::error;
#[cfg(test)]
mod test;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error {
Infinite,
NaN,
Overflow,
Underflow,
}
impl Error {
fn description_helper(&self) -> &str {
match *self {
Error::Infinite => "Cannot store infinite value in finite type",
Error::NaN => "Cannot store NaN in type which does not support it",
Error::Overflow => "Overflow during numeric conversion",
Error::Underflow => "Underflow during numeric conversion",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description_helper())
}
}
#[cfg(feature = "std")]
impl error::Error for Error {
fn description(&self) -> &str {
self.description_helper()
}
}
pub trait From<Src> {
type Output;
fn cast(Src) -> Self::Output;
}
macro_rules! fns {
($($ty:ident),+) => {
$(
#[inline]
pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
where $ty: From<T>
{
<$ty as From<T>>::cast(x)
}
)+
}
}
fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(feature = "x128")]
fns!(i128, u128);
macro_rules! promotion {
($($src:ty => $($dst: ty),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = $dst;
#[inline]
fn cast(src: $src) -> $dst {
src as $dst
}
}
)+
)+
}
}
macro_rules! half_promotion {
($($src:ty => $($dst:ty),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
if src < 0 {
Err(Error::Underflow)
} else {
Ok(src as $dst)
}
}
}
)+
)+
}
}
macro_rules! from_unsigned {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::$dst;
if src > $dst::MAX as $src {
Err(Error::Overflow)
} else {
Ok(src as $dst)
}
}
}
)+
)+
}
}
macro_rules! from_signed {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::$dst;
Err(if src < $dst::MIN as $src {
Error::Underflow
} else if src > $dst::MAX as $src {
Error::Overflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
macro_rules! from_float {
($($src:ident, $usrc:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::{$dst, $src};
Err(if src != src {
Error::NaN
} else if src == $src::INFINITY ||
src == $src::NEG_INFINITY {
Error::Infinite
} else if {
use core::mem::transmute;
let max = unsafe {
transmute::<_, $src>(transmute::<_, $usrc>($dst::MAX as $src) - 1)
};
src > max
} {
Error::Overflow
} else if $dst::MIN == 0 {
if src <= -1.0 {
Error::Underflow
} else {
return Ok(src as $dst);
}
} else if src < $dst::MIN as $src {
Error::Underflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
#[cfg(feature = "x128")]
macro_rules! from_float_dst {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
#[allow(unused_comparisons)]
fn cast(src: $src) -> Self::Output {
use core::{$dst, $src};
Err(if src != src {
Error::NaN
} else if src == $src::INFINITY ||
src == $src::NEG_INFINITY {
Error::Infinite
} else if ($dst::MIN == 0) && src <= -1.0 {
Error::Underflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
#[cfg(target_pointer_width = "32")]
mod _32 {
use {Error, From};
promotion! {
i8 => f32, f64, i8, i16, i32, isize, i64;
i16 => f32, f64, i16, i32, isize, i64;
i32 => f32, f64, i32, isize, i64;
isize => f32, f64, i32, isize, i64;
i64 => f32, f64, i64;
}
half_promotion! {
i8 => u8, u16, u32, usize, u64;
i16 => u16, u32, usize, u64;
i32 => u32, usize, u64;
isize => u32, usize, u64;
i64 => u64;
}
from_signed! {
i16 => i8, u8;
i32 => i8, i16, u8, u16;
isize => i8, i16, u8, u16;
i64 => i8, i16, i32, isize, u8, u16, u32, usize;
}
promotion! {
u8 => f32, f64, i16, i32, isize, i64, u8, u16, u32, usize, u64;
u16 => f32, f64, i32, isize, i64, u16, u32, usize, u64;
u32 => f32, f64, i64, u32, usize, u64;
usize => f32, f64, i64, u32, usize, u64;
u64 => f32, f64, u64;
}
from_unsigned! {
u8 => i8;
u16 => i8, i16, u8;
u32 => i8, i16, i32, isize, u8, u16;
usize => i8, i16, i32, isize, u8, u16;
u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize;
}
promotion! {
f32 => f32, f64;
f64 => f64;
}
from_float! {
f32, u32 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
f64, u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
}
}
#[cfg(target_pointer_width = "64")]
mod _64 {
use {Error, From};
promotion! {
i8 => f32, f64, i8, i16, i32, i64, isize;
i16 => f32, f64, i16, i32, i64, isize;
i32 => f32, f64, i32, i64, isize;
i64 => f32, f64, i64, isize;
isize => f32, f64, i64, isize;
}
half_promotion! {
i8 => u8, u16, u32, u64, usize;
i16 => u16, u32, u64, usize;
i32 => u32, u64, usize;
i64 => u64, usize;
isize => u64, usize;
}
from_signed! {
i16 => i8, u8;
i32 => i8, i16, u8, u16;
i64 => i8, i16, i32, u8, u16, u32;
isize => i8, i16, i32, u8, u16, u32;
}
promotion! {
u8 => f32, f64, i16, i32, i64, isize, u8, u16, u32, u64, usize;
u16 => f32, f64, i32, i64, isize, u16, u32, u64, usize;
u32 => f32, f64, i64, isize, u32, u64, usize;
u64 => f32, f64, u64, usize;
usize => f32, f64, u64, usize;
}
from_unsigned! {
u8 => i8;
u16 => i8, i16, u8;
u32 => i8, i16, i32, u8, u16;
u64 => i8, i16, i32, i64, isize, u8, u16, u32;
usize => i8, i16, i32, i64, isize, u8, u16, u32;
}
promotion! {
f32 => f32, f64;
f64 => f64;
}
from_float! {
f32, u32 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
f64, u64 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
}
}
#[cfg(feature = "x128")]
mod _x128 {
use {Error, From};
promotion! {
i8 => i128;
i16 => i128;
i32 => i128;
i64 => i128;
isize => i128;
i128 => f32, f64, i128;
}
half_promotion! {
i8 => u128;
i16 => u128;
i32 => u128;
i64 => u128;
isize => u128;
i128 => u128;
}
from_signed! {
i128 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
}
promotion! {
u8 => i128, u128;
u16 => i128, u128;
u32 => i128, u128;
u64 => i128, u128;
usize => i128, u128;
u128 => f64, u128;
}
from_unsigned! {
u128 => f32, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize;
}
from_float! {
f32, u32 => i128;
f64, u64 => i128, u128;
}
from_float_dst! {
f32 => u128;
}
}
impl From<f64> for f32 {
type Output = Result<f32, Error>;
#[inline]
fn cast(src: f64) -> Self::Output {
use core::{f32, f64};
if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
Ok(src as f32)
} else if src < f32::MIN as f64 {
Err(Error::Underflow)
} else if src > f32::MAX as f64 {
Err(Error::Overflow)
} else {
Ok(src as f32)
}
}
}