about summary refs log tree commit diff
path: root/src
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
parent27673610a60c4aa538a92647ec9125b04f653fe4 (diff)
downloadrust-c77166c68547863db87f38e6a47c33f343980ee8.tar.gz
rust-c77166c68547863db87f38e6a47c33f343980ee8.zip
rustc_const_eval: work around double rounding.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/const_val.rs53
-rw-r--r--src/librustc_const_eval/eval.rs109
-rw-r--r--src/librustc_const_math/err.rs18
-rw-r--r--src/librustc_const_math/float.rs173
-rw-r--r--src/librustc_const_math/lib.rs2
-rw-r--r--src/librustc_trans/mir/constant.rs11
-rw-r--r--src/test/run-pass/issue-32805.rs26
7 files changed, 291 insertions, 101 deletions
diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs
index 3621cb267d9..3482971cd19 100644
--- a/src/librustc/middle/const_val.rs
+++ b/src/librustc/middle/const_val.rs
@@ -12,14 +12,12 @@ use syntax::parse::token::InternedString;
 use syntax::ast;
 use std::rc::Rc;
 use hir::def_id::DefId;
-use std::hash;
-use std::mem::transmute;
 use rustc_const_math::*;
 use self::ConstVal::*;
 
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
 pub enum ConstVal {
-    Float(f64),
+    Float(ConstFloat),
     Integral(ConstInt),
     Str(InternedString),
     ByteStr(Rc<Vec<u8>>),
@@ -36,55 +34,10 @@ pub enum ConstVal {
     Dummy,
 }
 
-impl hash::Hash for ConstVal {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        match *self {
-            Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state),
-            Integral(a) => a.hash(state),
-            Str(ref a) => a.hash(state),
-            ByteStr(ref a) => a.hash(state),
-            Bool(a) => a.hash(state),
-            Struct(a) => a.hash(state),
-            Tuple(a) => a.hash(state),
-            Function(a) => a.hash(state),
-            Array(a, n) => { a.hash(state); n.hash(state) },
-            Repeat(a, n) => { a.hash(state); n.hash(state) },
-            Char(c) => c.hash(state),
-            Dummy => ().hash(state),
-        }
-    }
-}
-
-/// Note that equality for `ConstVal` 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 ConstVal {
-    fn eq(&self, other: &ConstVal) -> bool {
-        match (self, other) {
-            (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
-            (&Integral(a), &Integral(b)) => a == b,
-            (&Str(ref a), &Str(ref b)) => a == b,
-            (&ByteStr(ref a), &ByteStr(ref b)) => a == b,
-            (&Bool(a), &Bool(b)) => a == b,
-            (&Struct(a), &Struct(b)) => a == b,
-            (&Tuple(a), &Tuple(b)) => a == b,
-            (&Function(a), &Function(b)) => a == b,
-            (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
-            (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
-            (&Char(a), &Char(b)) => a == b,
-            (&Dummy, &Dummy) => true, // FIXME: should this be false?
-            _ => false,
-        }
-    }
-}
-
-impl Eq for ConstVal { }
-
 impl ConstVal {
     pub fn description(&self) -> &'static str {
         match *self {
-            Float(_) => "float",
+            Float(f) => f.description(),
             Integral(i) => i.description(),
             Str(_) => "string literal",
             ByteStr(_) => "byte string literal",
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 785b734b799..5637b44335e 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -621,18 +621,19 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?,
                eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) {
           (Float(a), Float(b)) => {
+            use std::cmp::Ordering::*;
             match op.node {
-              hir::BiAdd => Float(a + b),
-              hir::BiSub => Float(a - b),
-              hir::BiMul => Float(a * b),
-              hir::BiDiv => Float(a / b),
-              hir::BiRem => Float(a % b),
-              hir::BiEq => Bool(a == b),
-              hir::BiLt => Bool(a < b),
-              hir::BiLe => Bool(a <= b),
-              hir::BiNe => Bool(a != b),
-              hir::BiGe => Bool(a >= b),
-              hir::BiGt => Bool(a > b),
+              hir::BiAdd => Float(math!(e, a + b)),
+              hir::BiSub => Float(math!(e, a - b)),
+              hir::BiMul => Float(math!(e, a * b)),
+              hir::BiDiv => Float(math!(e, a / b)),
+              hir::BiRem => Float(math!(e, a % b)),
+              hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
+              hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
+              hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
+              hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
+              hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
+              hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
               _ => signal!(e, InvalidOpForFloats(op.node)),
             }
           }
@@ -1078,13 +1079,13 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
             }
         },
         ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() {
-            Infer(u) => Ok(Float(u as f64)),
-            InferSigned(i) => Ok(Float(i as f64)),
+            Infer(u) => Ok(Float(F64(u as f64))),
+            InferSigned(i) => Ok(Float(F64(i as f64))),
             _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
         },
         ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() {
-            Infer(u) => Ok(Float(u as f32 as f64)),
-            InferSigned(i) => Ok(Float(i as f32 as f64)),
+            Infer(u) => Ok(Float(F32(u as f32))),
+            InferSigned(i) => Ok(Float(F32(i as f32))),
             _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
         },
         ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
@@ -1097,13 +1098,35 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
     }
 }
 
-fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: f64, ty: ty::Ty) -> CastResult {
+fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              val: ConstFloat,
+                              ty: ty::Ty) -> CastResult {
     match ty.sty {
-        ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
-        ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty),
-        ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
-        ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)),
-        ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)),
+        ty::TyInt(_) | ty::TyUint(_) => {
+            let i = match val {
+                F32(f) if f >= 0.0 => Infer(f as u64),
+                FInfer { f64: f, .. } |
+                F64(f) if f >= 0.0 => Infer(f as u64),
+
+                F32(f) => InferSigned(f as i64),
+                FInfer { f64: f, .. } |
+                F64(f) => InferSigned(f as i64)
+            };
+
+            if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) {
+                return Err(CannotCast);
+            }
+
+            cast_const_int(tcx, i, ty)
+        }
+        ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
+            F32(f) => f as f64,
+            FInfer { f64: f, .. } | F64(f) => f
+        }))),
+        ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
+            F64(f) => f as f32,
+            FInfer { f32: f, .. } | F32(f) => f
+        }))),
         _ => Err(CannotCast),
     }
 }
@@ -1161,33 +1184,43 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
             infer(Infer(n), tcx, &ty::TyUint(ity)).map(Integral)
         },
 
-        LitKind::Float(ref n, _) |
+        LitKind::Float(ref n, fty) => {
+            Ok(Float(parse_float(n, Some(fty), span)))
+        }
         LitKind::FloatUnsuffixed(ref n) => {
-            if let Ok(x) = n.parse::<f64>() {
-                Ok(Float(x))
-            } else {
-                // FIXME(#31407) this is only necessary because float parsing is buggy
-                span_bug!(span, "could not evaluate float literal (see issue #31407)");
-            }
+            let fty_hint = match ty_hint.map(|t| &t.sty) {
+                Some(&ty::TyFloat(fty)) => Some(fty),
+                _ => None
+            };
+            Ok(Float(parse_float(n, fty_hint, span)))
         }
         LitKind::Bool(b) => Ok(Bool(b)),
         LitKind::Char(c) => Ok(Char(c)),
     }
 }
 
+fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFloat {
+    let val = match fty_hint {
+        Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32),
+        Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64),
+        None => {
+            num.parse::<f32>().and_then(|f32| {
+                num.parse::<f64>().map(|f64| {
+                    FInfer { f32: f32, f64: f64 }
+                })
+            })
+        }
+    };
+    val.unwrap_or_else(|_| {
+        // FIXME(#31407) this is only necessary because float parsing is buggy
+        span_bug!(span, "could not evaluate float literal (see issue #31407)");
+    })
+}
+
 pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
     match (a, b) {
         (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
-        (&Float(a), &Float(b)) => {
-            // This is pretty bad but it is the existing behavior.
-            Some(if a == b {
-                Ordering::Equal
-            } else if a < b {
-                Ordering::Less
-            } else {
-                Ordering::Greater
-            })
-        }
+        (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
         (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
         (&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
         (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
diff --git a/src/librustc_const_math/err.rs b/src/librustc_const_math/err.rs
index 9970810d4e2..e4eb0f2c97e 100644
--- a/src/librustc_const_math/err.rs
+++ b/src/librustc_const_math/err.rs
@@ -45,17 +45,17 @@ impl ConstMathErr {
         use self::Op::*;
         match *self {
             NotInRange => "inferred value out of range",
-            CmpBetweenUnequalTypes => "compared two integrals of different types",
-            UnequalTypes(Add) => "tried to add two integrals of different types",
-            UnequalTypes(Sub) => "tried to subtract two integrals of different types",
-            UnequalTypes(Mul) => "tried to multiply two integrals of different types",
-            UnequalTypes(Div) => "tried to divide two integrals of different types",
+            CmpBetweenUnequalTypes => "compared two values of different types",
+            UnequalTypes(Add) => "tried to add two values of different types",
+            UnequalTypes(Sub) => "tried to subtract two values of different types",
+            UnequalTypes(Mul) => "tried to multiply two values of different types",
+            UnequalTypes(Div) => "tried to divide two values of different types",
             UnequalTypes(Rem) => {
-                "tried to calculate the remainder of two integrals of different types"
+                "tried to calculate the remainder of two values of different types"
             },
-            UnequalTypes(BitAnd) => "tried to bitand two integrals of different types",
-            UnequalTypes(BitOr) => "tried to bitor two integrals of different types",
-            UnequalTypes(BitXor) => "tried to xor two integrals of different types",
+            UnequalTypes(BitAnd) => "tried to bitand two values of different types",
+            UnequalTypes(BitOr) => "tried to bitor two values of different types",
+            UnequalTypes(BitXor) => "tried to xor two values of different types",
             UnequalTypes(_) => unreachable!(),
             Overflow(Add) => "attempted to add with overflow",
             Overflow(Sub) => "attempted to subtract with overflow",
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
+            }
+        }
+    }
+}
diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs
index 59792d16e8b..741dd4107e0 100644
--- a/src/librustc_const_math/lib.rs
+++ b/src/librustc_const_math/lib.rs
@@ -32,11 +32,13 @@
 
 extern crate serialize as rustc_serialize; // used by deriving
 
+mod float;
 mod int;
 mod us;
 mod is;
 mod err;
 
+pub use float::*;
 pub use int::*;
 pub use us::*;
 pub use is::*;
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index d352a8241ac..f5fdd87b167 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -12,6 +12,7 @@ use llvm::{self, ValueRef};
 use rustc::middle::const_val::ConstVal;
 use rustc_const_eval::ErrKind;
 use rustc_const_math::ConstInt::*;
+use rustc_const_math::ConstFloat::*;
 use rustc_const_math::ConstMathErr;
 use rustc::hir::def_id::DefId;
 use rustc::infer::TransNormalize;
@@ -63,7 +64,9 @@ impl<'tcx> Const<'tcx> {
                              -> Const<'tcx> {
         let llty = type_of::type_of(ccx, ty);
         let val = match cv {
-            ConstVal::Float(v) => C_floating_f64(v, llty),
+            ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty),
+            ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
+            ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv),
             ConstVal::Bool(v) => C_bool(ccx, v),
             ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
             ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
@@ -81,14 +84,14 @@ impl<'tcx> Const<'tcx> {
                 let u = v.as_u64(ccx.tcx().sess.target.uint_type);
                 C_integral(Type::int(ccx), u, false)
             },
-            ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false),
-            ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true),
+            ConstVal::Integral(Infer(_)) |
+            ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv),
             ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
             ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
             ConstVal::Struct(_) | ConstVal::Tuple(_) |
             ConstVal::Array(..) | ConstVal::Repeat(..) |
             ConstVal::Function(_) => {
-                bug!("MIR must not use {:?} (which refers to a local ID)", cv)
+                bug!("MIR must not use `{:?}` (which refers to a local ID)", cv)
             }
             ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
             ConstVal::Dummy => bug!(),
diff --git a/src/test/run-pass/issue-32805.rs b/src/test/run-pass/issue-32805.rs
new file mode 100644
index 00000000000..ea49cf3e7be
--- /dev/null
+++ b/src/test/run-pass/issue-32805.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 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.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn const_mir() -> f32 { 9007199791611905.0 }
+
+#[rustc_no_mir]
+fn const_old() -> f32 { 9007199791611905.0 }
+
+fn main() {
+    let original = "9007199791611905.0"; // (1<<53)+(1<<29)+1
+    let expected = "9007200000000000";
+
+    assert_eq!(const_mir().to_string(), expected);
+    assert_eq!(const_old().to_string(), expected);
+    assert_eq!(original.parse::<f32>().unwrap().to_string(), expected);
+}