refactor(voice_hybrid): Phase B language-advantage pass — dichotomy wave

The duplicated exact-decimal readers behind round3/format_percent fold
into one Decimal6 struct (single {:.6} expansion + one banker's
round_up decision); the format_percent odd-kept-digit ladder
(end_with 1/3/5/7/9) becomes int parity. Behavior identical: battery
11 green, clippy -D warnings 0, live parity 1140/1140.
This commit is contained in:
Nick committed 2026-10-14 05:36:16 +01:00
1 parent 833d7af476
commit 6c1c4fea97
1 file changed
+48 -43
+48 -43
View File
@@ -208,34 +208,55 @@ const DIMENSIONS: [Dimension; 5] = [
// an integral value to i64/u64 on the Value path. i64 appears only for the
// `turns` counter, which the oracle keeps an int.)
/// A fixed 6-digit decimal expansion: the exact-decimal reader both
/// banker's-rounding helpers share. Trailing binary noise lives past digit
/// 12, so 6 digits capture the decision digit plus genuine nonzero tails
/// without ever flipping a halfway case.
struct Decimal6 {
neg: bool,
int_part: i64,
frac: [u8; 6],
}
impl Decimal6 {
fn of(x: f64, scale: f64) -> Option<Self> {
if !x.is_finite() {
return None;
}
let neg = x < 0.0;
let s = format!("{:.6}", (x * scale).abs());
let (int_s, frac_s) = match s.split_once('.') {
Some((i, f)) => (i, f),
None => (s.as_str(), "000000"),
};
let mut frac = [b'0'; 6];
for (i, b) in frac_s.bytes().take(6).enumerate() {
frac[i] = b;
}
Some(Self { neg, int_part: int_s.parse::<i64>().unwrap_or(0), frac })
}
/// Banker's decision at `pos`: round up iff the decision digit exceeds
/// 5, or equals 5 with a nonzero tail or an odd kept digit.
fn round_up(&self, pos: usize, kept_odd: bool) -> bool {
let d = (self.frac[pos] - b'0') as i64;
d > 5 || (d == 5 && (self.frac.iter().skip(pos + 1).any(|&b| b != b'0') || kept_odd))
}
}
/// Python `round(x, 3)` — banker's rounding (ties-to-even) on the decimal
/// representation. Implemented on the exact decimal expansion so halfway
/// cases (e.g. 0.0625 → 0.062, 0.1875 → 0.188) round the way CPython does,
/// independent of binary-float representation noise.
fn round3(x: f64) -> f64 {
if !x.is_finite() {
return x;
}
let neg = x < 0.0;
// 6 decimals capture the exact binary value's decimal expansion far
// beyond the 4th-decimal decision digit; trailing binary noise lives
// past digit 12 and never flips a genuine halfway case.
let s = format!("{:.6}", x.abs());
let (int_part, frac_part) = match s.split_once('.') {
Some((i, f)) => (i, f),
None => (s.as_str(), "000000"),
};
let frac: Vec<u8> = frac_part.bytes().take(6).collect();
// Decision digit (4th decimal) + whether any later digit is nonzero.
let d3 = (frac[3] - b'0') as i64;
let rest_nonzero = frac.iter().skip(4).any(|&b| b != b'0');
let round_up = d3 > 5 || (d3 == 5 && (rest_nonzero || (frac[2] - b'0') % 2 == 1));
let mut thousandths =
int_part.parse::<i64>().unwrap_or(0) * 1000 + (frac[0] - b'0') as i64 * 100
+ (frac[1] - b'0') as i64 * 10
+ (frac[2] - b'0') as i64
+ i64::from(round_up);
if neg {
let Some(dec) = Decimal6::of(x, 1.0) else { return x };
let kept_odd = (dec.frac[2] - b'0') % 2 == 1;
let mut thousandths = dec.int_part * 1000
+ (dec.frac[0] - b'0') as i64 * 100
+ (dec.frac[1] - b'0') as i64 * 10
+ (dec.frac[2] - b'0') as i64
+ i64::from(dec.round_up(3, kept_odd));
if dec.neg {
thousandths = -thousandths;
}
thousandths as f64 / 1000.0
@@ -246,26 +267,10 @@ fn round3(x: f64) -> f64 {
/// 289.999...), so the decision digit is read from the exact decimal
/// expansion exactly like `round3`.
fn format_percent(x: f64) -> String {
if !x.is_finite() {
return format!("{x:.0}%");
}
let neg = x < 0.0;
let scaled = (x * 100.0).abs();
let s = format!("{scaled:.6}");
let (int_part, frac_part) = match s.split_once('.') {
Some((i, f)) => (i, f),
None => (s.as_str(), "000000"),
};
let frac: Vec<u8> = frac_part.bytes().take(6).collect();
let d0 = (frac[0] - b'0') as i64;
let rest_nonzero = frac.iter().skip(1).any(|&b| b != b'0');
let round_up = d0 > 5 || (d0 == 5 && (rest_nonzero || int_part.ends_with('1')
|| int_part.ends_with('3')
|| int_part.ends_with('5')
|| int_part.ends_with('7')
|| int_part.ends_with('9')));
let mut whole = int_part.parse::<i64>().unwrap_or(0) + i64::from(round_up);
if neg {
let Some(dec) = Decimal6::of(x, 100.0) else { return format!("{x:.0}%") };
let kept_odd = dec.int_part % 2 != 0;
let mut whole = dec.int_part + i64::from(dec.round_up(0, kept_odd));
if dec.neg {
whole = -whole;
}
format!("{whole}%")