(&mut self, op: u16)
| 212 | } |
| 213 | |
| 214 | pub fn call_round(&mut self, op: u16) -> Result<(), VmErr> { |
| 215 | let args = self.pop_n(op as usize)?; |
| 216 | let v = match (args.first().copied(), args.get(1).copied()) { |
| 217 | (Some(o), Some(n)) if o.is_float() && n.is_int() => { |
| 218 | let x = o.as_float(); |
| 219 | let nd = n.as_int(); |
| 220 | if !x.is_finite() { |
| 221 | Val::float(x) |
| 222 | } else if nd >= 0 { |
| 223 | // Correctly-rounded decimal (round-half-even on the true value); avoids the double-rounding `x*10^n` would introduce (e.g. round(2.675, 2) -> 2.67). |
| 224 | let s = alloc::format!("{:.*}", (nd as usize).min(323), x); |
| 225 | Val::float(s.parse().unwrap_or(x)) |
| 226 | } else { |
| 227 | let factor = fpowi(10.0, (-nd) as i32); |
| 228 | Val::float(fround(x / factor) * factor) |
| 229 | } |
| 230 | } |
| 231 | (Some(o), None) if o.is_float() => { |
| 232 | // 1-arg round returns an int; promote via int_to_val so large results don't wrap. |
| 233 | let f = o.as_float(); |
| 234 | if f.is_nan() { return Err(cold_value("cannot convert float NaN to integer")); } |
| 235 | if f.is_infinite() { return Err(VmErr::Raised(alloc::string::String::from("OverflowError: cannot convert float infinity to integer"))); } |
| 236 | let r = fround(f); |
| 237 | if !(-1.7014118346046923e38..=1.7014118346046921e38).contains(&r) { return Err(cold_overflow()); } |
| 238 | self.int_to_val(Some(r as i128))? |
| 239 | } |
| 240 | // Ints/bools round to themselves; negative ndigits round to tens/hundreds. |
| 241 | (Some(o), n) if o.is_bool() || o.is_int() || (o.is_heap() && matches!(self.heap.get(o), HeapObj::LongInt(_))) => { |
| 242 | let i = if o.is_bool() { o.as_bool() as i128 } else { self.as_i128(o).ok_or(cold_type("round() requires a number"))? }; |
| 243 | let nd = match n { Some(n) if n.is_int() => n.as_int(), _ => 0 }; |
| 244 | let r = if nd < 0 { round_int_banker(i, (-nd) as u32) } else { i }; |
| 245 | self.int_to_val(Some(r))? |
| 246 | } |
| 247 | _ => return Err(cold_type("round() requires a number")), |
| 248 | }; |
| 249 | self.push(v); Ok(()) |
| 250 | } |
| 251 | |
| 252 | pub fn call_min(&mut self, op: u16, chunk: &crate::modules::parser::SSAChunk, slots: &mut [Val]) -> Result<(), VmErr> { self.call_minmax(op, true, chunk, slots) } |
| 253 | pub fn call_max(&mut self, op: u16, chunk: &crate::modules::parser::SSAChunk, slots: &mut [Val]) -> Result<(), VmErr> { self.call_minmax(op, false, chunk, slots) } |
no test coverage detected