about summary refs log tree commit diff
path: root/src/librustc_const_math/float.rs
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-06-01 12:22:07 +0300
committerEduard Burtescu <edy.burt@gmail.com>2016-06-05 19:18:28 +0300
commitc77166c68547863db87f38e6a47c33f343980ee8 (patch)
treee9664bdbc44440b48c302e0faaab82fcd3fc044b /src/librustc_const_math/float.rs
parent27673610a60c4aa538a92647ec9125b04f653fe4 (diff)
downloadrust-c77166c68547863db87f38e6a47c33f343980ee8.tar.gz
rust-c77166c68547863db87f38e6a47c33f343980ee8.zip
rustc_const_eval: work around double rounding.
Diffstat (limited to 'src/librustc_const_math/float.rs')
-rw-r--r--src/librustc_const_math/float.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/librustc_const_math/float.rs b/src/librustc_const_math/float.rs
new file mode 100644
index 00000000000..4610c183e1b
--- /dev/null
+++ b/src/librustc_const_math/float.rs
@@ -0,0 +1,173 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp::Ordering;
+use std::hash;
+use std::mem::transmute;
+
+use super::err::*;
+
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum ConstFloat {
+    F32(f32),
+    F64(f64),
+
+    // When the type isn't known, we have to operate on both possibilities.
+    FInfer {
+        f32: f32,
+        f64: f64
+    }
+}
+pub use self::ConstFloat::*;
+
+impl ConstFloat {
+    /// Description of the type, not the value
+    pub fn description(&self) -> &'static str {
+        match *self {
+            FInfer {..} => "float",
+            F32(_) => "f32",
+            F64(_) => "f64",
+        }
+    }
+
+    pub fn is_nan(&self) -> bool {
+        match *self {
+            F32(f) => f.is_nan(),
+            F64(f) => f.is_nan(),
+            FInfer { f32, f64 } => f32.is_nan() || f64.is_nan()
+        }
+    }
+
+    /// Compares the values if they are of the same type
+    pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
+        match (self, rhs) {
+            (F64(a), F64(b)) |
+            (F64(a), FInfer { f64: b, .. }) |
+            (FInfer { f64: a, .. }, F64(b)) |
+            (FInfer { f64: a, .. }, FInfer { f64: b, .. })  => {
+                // This is pretty bad but it is the existing behavior.
+                Ok(if a == b {
+                    Ordering::Equal
+                } else if a < b {
+                    Ordering::Less
+                } else {
+                    Ordering::Greater
+                })
+            }
+
+            (F32(a), F32(b)) |
+            (F32(a), FInfer { f32: b, .. }) |
+            (FInfer { f32: a, .. }, F32(b)) => {
+                Ok(if a == b {
+                    Ordering::Equal
+                } else if a < b {
+                    Ordering::Less
+                } else {
+                    Ordering::Greater
+                })
+            }
+
+            _ => Err(CmpBetweenUnequalTypes),
+        }
+    }
+}
+
+/// Note that equality for `ConstFloat` means that the it is the same
+/// constant, not that the rust values are equal. In particular, `NaN
+/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
+/// are considering unequal).
+impl PartialEq for ConstFloat {
+    fn eq(&self, other: &Self) -> bool {
+        match (*self, *other) {
+            (F64(a), F64(b)) |
+            (F64(a), FInfer { f64: b, .. }) |
+            (FInfer { f64: a, .. }, F64(b)) |
+            (FInfer { f64: a, .. }, FInfer { f64: b, .. }) => {
+                unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}
+            }
+            (F32(a), F32(b)) => {
+                unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
+            }
+            _ => false
+        }
+    }
+}
+
+impl Eq for ConstFloat {}
+
+impl hash::Hash for ConstFloat {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        match *self {
+            F64(a) | FInfer { f64: a, .. } => {
+                unsafe { transmute::<_,u64>(a) }.hash(state)
+            }
+            F32(a) => {
+                unsafe { transmute::<_,u32>(a) }.hash(state)
+            }
+        }
+    }
+}
+
+impl ::std::fmt::Display for ConstFloat {
+    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
+        match *self {
+            FInfer { f64, .. } => write!(fmt, "{}", f64),
+            F32(f) => write!(fmt, "{}f32", f),
+            F64(f) => write!(fmt, "{}f64", f),
+        }
+    }
+}
+
+macro_rules! derive_binop {
+    ($op:ident, $func:ident) => {
+        impl ::std::ops::$op for ConstFloat {
+            type Output = Result<Self, ConstMathErr>;
+            fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
+                match (self, rhs) {
+                    (F32(a), F32(b)) |
+                    (F32(a), FInfer { f32: b, .. }) |
+                    (FInfer { f32: a, .. }, F32(b)) => Ok(F32(a.$func(b))),
+
+                    (F64(a), F64(b)) |
+                    (FInfer { f64: a, .. }, F64(b)) |
+                    (F64(a), FInfer { f64: b, .. }) => Ok(F64(a.$func(b))),
+
+                    (FInfer { f32: a32, f64: a64 },
+                     FInfer { f32: b32, f64: b64 }) => Ok(FInfer {
+                        f32: a32.$func(b32),
+                        f64: a64.$func(b64)
+                    }),
+
+                    _ => Err(UnequalTypes(Op::$op)),
+                }
+            }
+        }
+    }
+}
+
+derive_binop!(Add, add);
+derive_binop!(Sub, sub);
+derive_binop!(Mul, mul);
+derive_binop!(Div, div);
+derive_binop!(Rem, rem);
+
+impl ::std::ops::Neg for ConstFloat {
+    type Output = Self;
+    fn neg(self) -> Self {
+        match self {
+            F32(f) => F32(-f),
+            F64(f) => F64(-f),
+            FInfer { f32, f64 } => FInfer {
+                f32: -f32,
+                f64: -f64
+            }
+        }
+    }
+}