mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
Implement our own small-integer optimization (#7584)
## Summary This is a follow-up to #7469 that attempts to achieve similar gains, but without introducing malachite. Instead, this PR removes the `BigInt` type altogether, instead opting for a simple enum that allows us to store small integers directly and only allocate for values greater than `i64`: ```rust /// A Python integer literal. Represents both small (fits in an `i64`) and large integers. #[derive(Clone, PartialEq, Eq, Hash)] pub struct Int(Number); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Number { /// A "small" number that can be represented as an `i64`. Small(i64), /// A "large" number that cannot be represented as an `i64`. Big(Box<str>), } impl std::fmt::Display for Number { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Number::Small(value) => write!(f, "{value}"), Number::Big(value) => write!(f, "{value}"), } } } ``` We typically don't care about numbers greater than `isize` -- our only uses are comparisons against small constants (like `1`, `2`, `3`, etc.), so there's no real loss of information, except in one or two rules where we're now a little more conservative (with the worst-case being that we don't flag, e.g., an `itertools.pairwise` that uses an extremely large value for the slice start constant). For simplicity, a few diagnostics now show a dedicated message when they see integers that are out of the supported range (e.g., `outdated-version-block`). An additional benefit here is that we get to remove a few dependencies, especially `num-bigint`. ## Test Plan `cargo test`
This commit is contained in:
parent
65aebf127a
commit
93b5d8a0fb
40 changed files with 707 additions and 385 deletions
|
@ -17,7 +17,6 @@ hexf-parse = "0.2.1"
|
|||
is-macro.workspace = true
|
||||
itertools = { workspace = true }
|
||||
lexical-parse-float = { version = "0.8.0", features = ["format"] }
|
||||
num-traits = { workspace = true }
|
||||
unic-ucd-category = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::Case;
|
||||
use num_traits::{Float, Zero};
|
||||
use std::f64;
|
||||
|
||||
use crate::Case;
|
||||
|
||||
pub fn parse_str(literal: &str) -> Option<f64> {
|
||||
parse_inner(literal.trim().as_bytes())
|
||||
}
|
||||
|
@ -244,46 +244,6 @@ pub fn from_hex(s: &str) -> Option<f64> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_hex(value: f64) -> String {
|
||||
let (mantissa, exponent, sign) = value.integer_decode();
|
||||
let sign_fmt = if sign < 0 { "-" } else { "" };
|
||||
match value {
|
||||
value if value.is_zero() => format!("{sign_fmt}0x0.0p+0"),
|
||||
value if value.is_infinite() => format!("{sign_fmt}inf"),
|
||||
value if value.is_nan() => "nan".to_owned(),
|
||||
_ => {
|
||||
const BITS: i16 = 52;
|
||||
const FRACT_MASK: u64 = 0xf_ffff_ffff_ffff;
|
||||
format!(
|
||||
"{}{:#x}.{:013x}p{:+}",
|
||||
sign_fmt,
|
||||
mantissa >> BITS,
|
||||
mantissa & FRACT_MASK,
|
||||
exponent + BITS
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::float_cmp)]
|
||||
fn test_to_hex() {
|
||||
use rand::Rng;
|
||||
for _ in 0..20000 {
|
||||
let bytes = rand::thread_rng().gen::<[u64; 1]>();
|
||||
let f = f64::from_bits(bytes[0]);
|
||||
if !f.is_finite() {
|
||||
continue;
|
||||
}
|
||||
let hex = to_hex(f);
|
||||
// println!("{} -> {}", f, hex);
|
||||
let roundtrip = hexf_parse::parse_hexf64(&hex, false).unwrap();
|
||||
// println!(" -> {}", roundtrip);
|
||||
|
||||
assert_eq!(f, roundtrip, "{f} {hex} {roundtrip}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_trailing_zeros() {
|
||||
assert!(remove_trailing_zeros(String::from("100")) == *"1");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue