Merge pull request #248 from ehuss/float-0.5
0.5: Support floats nan, inf, and +/-0.0.
This commit is contained in:
commit
816774077c
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::error;
|
use std::error;
|
||||||
|
use std::f64;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
@ -882,6 +883,14 @@ impl<'a> Deserializer<'a> {
|
||||||
}
|
}
|
||||||
_ => Err(self.error(at, ErrorKind::NumberInvalid)),
|
_ => Err(self.error(at, ErrorKind::NumberInvalid)),
|
||||||
}
|
}
|
||||||
|
} else if s == "inf" {
|
||||||
|
Ok(Value { e: E::Float(f64::INFINITY), start: start, end: end })
|
||||||
|
} else if s == "-inf" {
|
||||||
|
Ok(Value { e: E::Float(f64::NEG_INFINITY), start: start, end: end })
|
||||||
|
} else if s == "nan" {
|
||||||
|
Ok(Value { e: E::Float(f64::NAN), start: start, end: end })
|
||||||
|
} else if s == "-nan" {
|
||||||
|
Ok(Value { e: E::Float(-f64::NAN), start: start, end: end })
|
||||||
} else {
|
} else {
|
||||||
self.integer(s).map(|f| Value { e: E::Integer(f), start: start, end: end })
|
self.integer(s).map(|f| Value { e: E::Integer(f), start: start, end: end })
|
||||||
}
|
}
|
||||||
|
|
49
src/ser.rs
49
src/ser.rs
|
@ -737,6 +737,27 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! serialize_float {
|
||||||
|
($this:expr, $v:expr) => {{
|
||||||
|
$this.emit_key("float")?;
|
||||||
|
if ($v.is_nan() || $v == 0.0) && $v.is_sign_negative() {
|
||||||
|
drop(write!($this.dst, "-"));
|
||||||
|
}
|
||||||
|
if $v.is_nan() {
|
||||||
|
drop(write!($this.dst, "nan"));
|
||||||
|
} else {
|
||||||
|
drop(write!($this.dst, "{}", $v));
|
||||||
|
}
|
||||||
|
if $v % 1.0 == 0.0 {
|
||||||
|
drop(write!($this.dst, ".0"));
|
||||||
|
}
|
||||||
|
if let State::Table { .. } = $this.state {
|
||||||
|
$this.dst.push_str("\n");
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
|
impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
@ -785,35 +806,11 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_f32(self, v: f32) -> Result<(), Self::Error> {
|
fn serialize_f32(self, v: f32) -> Result<(), Self::Error> {
|
||||||
if !v.is_finite() {
|
serialize_float!(self, v)
|
||||||
return Err(Error::NumberInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_key("float")?;
|
|
||||||
drop(write!(self.dst, "{}", v));
|
|
||||||
if v % 1.0 == 0.0 {
|
|
||||||
drop(write!(self.dst, ".0"));
|
|
||||||
}
|
|
||||||
if let State::Table { .. } = self.state {
|
|
||||||
self.dst.push_str("\n");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_f64(self, v: f64) -> Result<(), Self::Error> {
|
fn serialize_f64(self, v: f64) -> Result<(), Self::Error> {
|
||||||
if !v.is_finite() {
|
serialize_float!(self, v)
|
||||||
return Err(Error::NumberInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_key("float")?;
|
|
||||||
drop(write!(self.dst, "{}", v));
|
|
||||||
if v % 1.0 == 0.0 {
|
|
||||||
drop(write!(self.dst, ".0"));
|
|
||||||
}
|
|
||||||
if let State::Table { .. } = self.state {
|
|
||||||
self.dst.push_str("\n");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_char(self, v: char) -> Result<(), Self::Error> {
|
fn serialize_char(self, v: char) -> Result<(), Self::Error> {
|
||||||
|
|
|
@ -4,5 +4,6 @@ use rustc_version::{version, Version};
|
||||||
fn main() {
|
fn main() {
|
||||||
if version().unwrap() >= Version::parse("1.20.0").unwrap() {
|
if version().unwrap() >= Version::parse("1.20.0").unwrap() {
|
||||||
println!(r#"cargo:rustc-cfg=feature="test-quoted-keys-in-macro""#);
|
println!(r#"cargo:rustc-cfg=feature="test-quoted-keys-in-macro""#);
|
||||||
|
println!(r#"cargo:rustc-cfg=feature="test-nan-sign""#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
79
test-suite/tests/float.rs
Normal file
79
test-suite/tests/float.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
extern crate toml;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
use toml::Value;
|
||||||
|
|
||||||
|
macro_rules! float_inf_tests {
|
||||||
|
($ty:ty) => {{
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct S {
|
||||||
|
sf1: $ty,
|
||||||
|
sf2: $ty,
|
||||||
|
sf3: $ty,
|
||||||
|
sf4: $ty,
|
||||||
|
sf5: $ty,
|
||||||
|
sf6: $ty,
|
||||||
|
sf7: $ty,
|
||||||
|
sf8: $ty,
|
||||||
|
}
|
||||||
|
let inf: S = toml::from_str(
|
||||||
|
r"
|
||||||
|
# infinity
|
||||||
|
sf1 = inf # positive infinity
|
||||||
|
sf2 = +inf # positive infinity
|
||||||
|
sf3 = -inf # negative infinity
|
||||||
|
|
||||||
|
# not a number
|
||||||
|
sf4 = nan # actual sNaN/qNaN encoding is implementation specific
|
||||||
|
sf5 = +nan # same as `nan`
|
||||||
|
sf6 = -nan # valid, actual encoding is implementation specific
|
||||||
|
|
||||||
|
# zero
|
||||||
|
sf7 = +0.0
|
||||||
|
sf8 = -0.0
|
||||||
|
").expect("Parse infinities.");
|
||||||
|
|
||||||
|
assert!(inf.sf1.is_infinite());
|
||||||
|
assert!(inf.sf1.is_sign_positive());
|
||||||
|
assert!(inf.sf2.is_infinite());
|
||||||
|
assert!(inf.sf2.is_sign_positive());
|
||||||
|
assert!(inf.sf3.is_infinite());
|
||||||
|
assert!(inf.sf3.is_sign_negative());
|
||||||
|
|
||||||
|
assert!(inf.sf4.is_nan());
|
||||||
|
assert!(inf.sf4.is_sign_positive());
|
||||||
|
assert!(inf.sf5.is_nan());
|
||||||
|
assert!(inf.sf5.is_sign_positive());
|
||||||
|
assert!(inf.sf6.is_nan());
|
||||||
|
assert!(inf.sf6.is_sign_negative());
|
||||||
|
|
||||||
|
assert_eq!(inf.sf7, 0.0);
|
||||||
|
assert!(inf.sf7.is_sign_positive());
|
||||||
|
assert_eq!(inf.sf8, 0.0);
|
||||||
|
assert!(inf.sf8.is_sign_negative());
|
||||||
|
|
||||||
|
let s = toml::to_string(&inf).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
s, "\
|
||||||
|
sf1 = inf
|
||||||
|
sf2 = inf
|
||||||
|
sf3 = -inf
|
||||||
|
sf4 = nan
|
||||||
|
sf5 = nan
|
||||||
|
sf6 = -nan
|
||||||
|
sf7 = 0.0
|
||||||
|
sf8 = -0.0
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
toml::from_str::<Value>(&s).expect("roundtrip");
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "test-nan-sign")]
|
||||||
|
fn float_inf() {
|
||||||
|
float_inf_tests!(f32);
|
||||||
|
float_inf_tests!(f64);
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
extern crate toml;
|
|
||||||
|
|
||||||
use std::f64;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_float_encode() {
|
|
||||||
fn bad(value: toml::Value) {
|
|
||||||
assert!(toml::to_string(&value).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
bad(toml::Value::Float(f64::INFINITY));
|
|
||||||
bad(toml::Value::Float(f64::NEG_INFINITY));
|
|
||||||
bad(toml::Value::Float(f64::NAN));
|
|
||||||
}
|
|
|
@ -10,8 +10,5 @@ fn bad() {
|
||||||
bad("a = 1__1");
|
bad("a = 1__1");
|
||||||
bad("a = 1_");
|
bad("a = 1_");
|
||||||
bad("''");
|
bad("''");
|
||||||
bad("a = nan");
|
|
||||||
bad("a = -inf");
|
|
||||||
bad("a = inf");
|
|
||||||
bad("a = 9e99999");
|
bad("a = 9e99999");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
"answer": {"type": "integer", "value": "42"},
|
"answer": {"type": "integer", "value": "42"},
|
||||||
"neganswer": {"type": "integer", "value": "-42"}
|
"neganswer": {"type": "integer", "value": "-42"},
|
||||||
|
"neg_zero": {"type": "integer", "value": "0"},
|
||||||
|
"pos_zero": {"type": "integer", "value": "0"}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
answer = 42
|
answer = 42
|
||||||
neganswer = -42
|
neganswer = -42
|
||||||
|
neg_zero = -0
|
||||||
|
pos_zero = +0
|
||||||
|
|
Loading…
Reference in a new issue