Skip to content

Commit fe21b9d

Browse files
authored
fix(unparser): eagerly unparse float constants to scientific notation (#15)
* fix(unparser): eagerly unparse float constants to scientific notation * fix build
1 parent 4e9dc21 commit fe21b9d

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ rust-version = "1.72.1"
1313
transformer = []
1414

1515
[dependencies]
16-
rustpython-ast = { version = ">=0.4.0" }
16+
rustpython-ast = { version = ">=0.4.0", features = ["rustpython-literal"] }
1717
rustpython-literal = ">=0.4.0"
1818

1919
[dev-dependencies]

src/unparser.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ impl Unparser {
10571057
}
10581058

10591059
fn _unparse_constant(&mut self, constant: &Constant) {
1060+
let inf_str = "1e309";
10601061
return match constant {
10611062
Constant::Tuple(values) => {
10621063
self.write_str("(");
@@ -1094,8 +1095,37 @@ impl Unparser {
10941095
self.write_str(&escaped);
10951096
}
10961097
Constant::None => self.write_str("None"),
1097-
Constant::Complex { real, imag: _ } => self.write_str(&real.to_string()),
1098-
Constant::Float(value) => self.write_str(&value.to_string()),
1098+
Constant::Complex { real, imag } => {
1099+
if real.is_infinite() || imag.is_infinite() {
1100+
self.write_str(&constant.to_string().replace("inf", &inf_str));
1101+
} else {
1102+
self.write_str(&constant.to_string());
1103+
}
1104+
}
1105+
Constant::Float(value) => {
1106+
if value.is_infinite() {
1107+
self.write_str(inf_str);
1108+
} else {
1109+
let mut str_value = value.to_string();
1110+
if value.fract() == 0.0 {
1111+
let mut trailing_zeroes = 0;
1112+
while str_value.ends_with("0") {
1113+
str_value.pop();
1114+
trailing_zeroes += 1;
1115+
}
1116+
str_value = format!("{}e{}", str_value, trailing_zeroes);
1117+
} else if str_value.starts_with(&format!("0.{}", "0".repeat(5))) {
1118+
let mut trimmed = str_value[2..].to_owned();
1119+
let mut factor = 1;
1120+
while trimmed.starts_with("0") {
1121+
trimmed = trimmed[1..].to_owned();
1122+
factor += 1;
1123+
}
1124+
str_value = format!("{}e-{}", trimmed, factor);
1125+
}
1126+
self.write_str(&str_value);
1127+
}
1128+
}
10991129
};
11001130
}
11011131

test_files/constants.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
11
escaped_string_expr = "'\"'''\"\"\"{}\\"
22
escaped_unicode_expr = u"'\"'''\"\"\"{}\\" # fmt: skip
33
escaped_bytes_expr = b"'\"'''\"\"\"{}\\"
4+
5+
6+
# real code from yaml.constructor
7+
# https://github.com/yaml/pyyaml/blob/69c141adcf805c5ebdc9ba519927642ee5c7f639/lib/yaml/constructor.py#L265
8+
inf_value = 1e300
9+
10+
# if inf_value would not be initialized in scientific notation
11+
# the following loop would run for a long time
12+
while inf_value != inf_value * inf_value:
13+
inf_value *= inf_value
14+
15+
scientific_fraction = 1e-69
16+
17+
complex_number = 3 + 4j

0 commit comments

Comments
 (0)