diff --git a/crates/doc/src/expr.rs b/crates/doc/src/expr.rs index d7f9b7cbdd..8a95cda3a1 100644 --- a/crates/doc/src/expr.rs +++ b/crates/doc/src/expr.rs @@ -972,7 +972,11 @@ fn call(f: &str, args: &[Value], d: Defaults) -> Result { let a = one()?; unit_fn(a.v.abs(), a) } - "sign" => Value::num(one()?.v.signum()), + "sign" => { + let x = one()?.v; + // `f64::signum` gives 1 for +0 and -1 for -0; the sign of zero is 0. + Value::num(if x == 0.0 { 0.0 } else { x.signum() }) + } "floor" | "ceil" | "round" | "trunc" => { let a = one()?; // Round in the value's own default unit (round(2.4 in) = 2 in for an inch parameter). @@ -1430,6 +1434,16 @@ mod tests { } } + /// `sign(0)` is 0, not 1 (Rust's `signum` takes the sign bit, so it calls zero positive). + #[test] + fn sign_of_zero_is_zero() { + for (e, want) in + [("sign(0)", 0.0), ("sign(-0)", 0.0), ("sign(0 mm)", 0.0), ("sign(5)", 1.0), ("sign(-2 mm)", -1.0), ("sign(width - 40)", 0.0)] + { + assert_eq!(ev(e, Kind::Unitless).unwrap(), want, "{e}"); + } + } + /// A count from a length (`(L - 20) / 210`, the 210 read as unit-less) names the fix (#5901). #[test] fn a_length_where_a_count_is_expected_says_to_divide_by_a_length() {