| 288 | } |
| 289 | |
| 290 | pub fn call_sum(&mut self, op: u16) -> Result<(), VmErr> { |
| 291 | let args = self.pop_n(op as usize)?; |
| 292 | if args.is_empty() { return Err(cold_type("sum() requires at least 1 argument")); } |
| 293 | let start = if args.len() > 1 { args[1] } else { Val::int(0) }; |
| 294 | let mut cur = self.iter_cursor(args[0])?; |
| 295 | let mut acc = start; |
| 296 | // Once a float enters, switch to Neumaier compensated summation (CPython 3.12+). |
| 297 | let mut fstate: Option<(f64, f64)> = if start.is_float() { Some((start.as_float(), 0.0)) } else { None }; |
| 298 | while let Some(item) = cur.next(&mut self.heap)? { |
| 299 | match fstate { |
| 300 | Some((s, c)) => match self.to_f64_coerce(item) { |
| 301 | Ok(x) => fstate = Some(neumaier(s, c, x)), |
| 302 | // Non-numeric after floats: let add_vals raise the proper TypeError. |
| 303 | Err(_) => { acc = self.add_vals(Val::float(s + c), item)?; fstate = None; } |
| 304 | }, |
| 305 | None if (item.is_float() || acc.is_float()) |
| 306 | && self.to_f64_coerce(acc).is_ok() && self.to_f64_coerce(item).is_ok() => { |
| 307 | let base = self.to_f64_coerce(acc).unwrap(); |
| 308 | let x = self.to_f64_coerce(item).unwrap(); |
| 309 | fstate = Some(neumaier(base, 0.0, x)); |
| 310 | } |
| 311 | None => { acc = self.add_vals(acc, item)?; } |
| 312 | } |
| 313 | } |
| 314 | let result = match fstate { Some((s, c)) => Val::float(s + c), None => acc }; |
| 315 | self.push(result); Ok(()) |
| 316 | } |
| 317 | |
| 318 | pub fn call_range(&mut self, op: u16) -> Result<(), VmErr> { |
| 319 | // Fold in UnpackArgs spread so `range(*args)` sees the real argument count. |