Merge pull request #1273 from jmg7173/master

Add floating point formatting
This commit is contained in:
Noah
2019-08-16 03:29:31 -05:00
committed by GitHub
3 changed files with 97 additions and 4 deletions

View File

@@ -212,6 +212,13 @@ assert "%(first)s %(second)s" % {'second': 'World!', 'first': "Hello,"} == "Hell
assert "%(key())s" % {'key()': 'aaa'}
assert "%s %a %r" % (f, f, f) == "str(Foo) repr(Foo) repr(Foo)"
assert "repr() shows quotes: %r; str() doesn't: %s" % ("test1", "test2") == "repr() shows quotes: 'test1'; str() doesn't: test2"
assert "%f" % (1.2345) == "1.234500"
assert "%+f" % (1.2345) == "+1.234500"
assert "% f" % (1.2345) == " 1.234500"
assert "%f" % (-1.2345) == "-1.234500"
assert "%f" % (1.23456789012) == "1.234568"
assert "%f" % (123) == "123.000000"
assert "%f" % (-123) == "-123.000000"
assert_raises(TypeError, lambda: "My name is %s and I'm %(age)d years old" % ("Foo", 25), msg="format requires a mapping")
assert_raises(TypeError, lambda: "My name is %(name)s" % "Foo", msg="format requires a mapping")

View File

@@ -209,6 +209,44 @@ impl CFormatSpec {
self.fill_string(format!("{}{}", prefix, magnitude_string), ' ', None)
}
}
pub fn format_float(&self, num: f64) -> String {
let magnitude = num.abs();
let sign_string = if num.is_sign_positive() {
if self.flags.contains(CConversionFlags::SIGN_CHAR) {
"+"
} else if self.flags.contains(CConversionFlags::BLANK_SIGN) {
" "
} else {
""
}
} else {
"-"
};
// TODO: Support precision
let magnitude_string = format!("{:.6}", magnitude);
if self.flags.contains(CConversionFlags::ZERO_PAD) {
let fill_char = if !self.flags.contains(CConversionFlags::LEFT_ADJUST) {
'0'
} else {
' '
};
format!(
"{}{}",
sign_string,
self.fill_string(
magnitude_string,
fill_char,
Some(sign_string.chars().count())
)
)
} else {
self.fill_string(format!("{}{}", sign_string, magnitude_string), ' ', None)
}
}
}
#[derive(Debug, PartialEq)]
@@ -763,6 +801,42 @@ mod tests {
);
}
#[test]
fn test_parse_and_format_float() {
assert_eq!(
"%f".parse::<CFormatSpec>()
.unwrap()
.format_float(f64::from(1.2345)),
"1.234500".to_string()
);
assert_eq!(
"%+f"
.parse::<CFormatSpec>()
.unwrap()
.format_float(f64::from(1.2345)),
"+1.234500".to_string()
);
assert_eq!(
"% f"
.parse::<CFormatSpec>()
.unwrap()
.format_float(f64::from(1.2345)),
" 1.234500".to_string()
);
assert_eq!(
"%f".parse::<CFormatSpec>()
.unwrap()
.format_float(f64::from(-1.2345)),
"-1.234500".to_string()
);
assert_eq!(
"%f".parse::<CFormatSpec>()
.unwrap()
.format_float(f64::from(1.2345678901)),
"1.234568".to_string()
);
}
#[test]
fn test_format_parse() {
let fmt = "Hello, my name is %s and I'm %d years old";

View File

@@ -28,6 +28,7 @@ use crate::vm::VirtualMachine;
use super::objbytes::PyBytes;
use super::objdict::PyDict;
use super::objfloat;
use super::objint::{self, PyInt};
use super::objiter;
use super::objnone::PyNone;
@@ -1225,6 +1226,21 @@ fn do_cformat_specifier(
}
Ok(format_spec.format_number(objint::get_value(&obj)))
}
CFormatType::Float(_) => {
if objtype::isinstance(&obj, &vm.ctx.float_type()) {
Ok(format_spec.format_float(objfloat::get_value(&obj)))
} else if objtype::isinstance(&obj, &vm.ctx.int_type()) {
Ok(format_spec.format_float(objint::get_value(&obj).to_f64().unwrap()))
} else {
let required_type_string = "an floating point or integer";
Err(vm.new_type_error(format!(
"%{} format: {} is required, not {}",
format_spec.format_char,
required_type_string,
obj.class()
)))
}
}
CFormatType::Character => {
let char_string = {
if objtype::isinstance(&obj, &vm.ctx.int_type()) {
@@ -1251,10 +1267,6 @@ fn do_cformat_specifier(
format_spec.precision = Some(CFormatQuantity::Amount(1));
Ok(format_spec.format_string(char_string))
}
_ => Err(vm.new_not_implemented_error(format!(
"Not yet implemented for %{}",
format_spec.format_char
))),
}
}