diff --git a/crates/typst/src/foundations/calc.rs b/crates/typst/src/foundations/calc.rs index f1faa73dd..e91dd3aee 100644 --- a/crates/typst/src/foundations/calc.rs +++ b/crates/typst/src/foundations/calc.rs @@ -615,28 +615,26 @@ pub fn lcm( /// /// If the number is already an integer, it is returned unchanged. /// -/// Note that this function will return the same type as the operand. That is, -/// applying `floor` to a [`float`] will return a `float`, and to a [`decimal`], -/// another `decimal`. You may explicitly convert the output of this function to -/// an integer with [`int`], but note that such a conversion will error if the -/// `float` or `decimal` is larger than the maximum 64-bit signed integer or -/// smaller than the minimum integer. +/// Note that this function will always return an [integer]($int), and will +/// error if the resulting [`float`] or [`decimal`] is larger than the maximum +/// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #assert(calc.floor(3) == 3) -/// #assert(calc.floor(3.14) == 3.0) -/// #assert(calc.floor(decimal("-3.14")) == decimal("-4")) +/// #assert(calc.floor(3.14) == 3) +/// #assert(calc.floor(decimal("-3.14")) == -4) /// #calc.floor(500.1) /// ``` #[func] pub fn floor( /// The number to round down. value: DecNum, -) -> DecNum { +) -> StrResult { match value { - DecNum::Int(n) => DecNum::Int(n), - DecNum::Float(n) => DecNum::Float(n.floor()), - DecNum::Decimal(n) => DecNum::Decimal(n.floor()), + DecNum::Int(n) => Ok(n), + DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.floor()) + .map_err(|_| too_large())?), + DecNum::Decimal(n) => Ok(i64::try_from(n.floor()).map_err(|_| too_large())?), } } @@ -644,28 +642,26 @@ pub fn floor( /// /// If the number is already an integer, it is returned unchanged. /// -/// Note that this function will return the same type as the operand. That is, -/// applying `ceil` to a [`float`] will return a `float`, and to a [`decimal`], -/// another `decimal`. You may explicitly convert the output of this function to -/// an integer with [`int`], but note that such a conversion will error if the -/// `float` or `decimal` is larger than the maximum 64-bit signed integer or -/// smaller than the minimum integer. +/// Note that this function will always return an [integer]($int), and will +/// error if the resulting [`float`] or [`decimal`] is larger than the maximum +/// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #assert(calc.ceil(3) == 3) /// #assert(calc.ceil(3.14) == 4) -/// #assert(calc.ceil(decimal("-3.14")) == decimal("-3")) +/// #assert(calc.ceil(decimal("-3.14")) == -3) /// #calc.ceil(500.1) /// ``` #[func] pub fn ceil( /// The number to round up. value: DecNum, -) -> DecNum { +) -> StrResult { match value { - DecNum::Int(n) => DecNum::Int(n), - DecNum::Float(n) => DecNum::Float(n.ceil()), - DecNum::Decimal(n) => DecNum::Decimal(n.ceil()), + DecNum::Int(n) => Ok(n), + DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.ceil()) + .map_err(|_| too_large())?), + DecNum::Decimal(n) => Ok(i64::try_from(n.ceil()).map_err(|_| too_large())?), } } @@ -673,28 +669,26 @@ pub fn ceil( /// /// If the number is already an integer, it is returned unchanged. /// -/// Note that this function will return the same type as the operand. That is, -/// applying `trunc` to a [`float`] will return a `float`, and to a [`decimal`], -/// another `decimal`. You may explicitly convert the output of this function to -/// an integer with [`int`], but note that such a conversion will error if the -/// `float` or `decimal` is larger than the maximum 64-bit signed integer or -/// smaller than the minimum integer. +/// Note that this function will always return an [integer]($int), and will +/// error if the resulting [`float`] or [`decimal`] is larger than the maximum +/// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #assert(calc.trunc(3) == 3) -/// #assert(calc.trunc(-3.7) == -3.0) -/// #assert(calc.trunc(decimal("8493.12949582390")) == decimal("8493")) +/// #assert(calc.trunc(-3.7) == -3) +/// #assert(calc.trunc(decimal("8493.12949582390")) == 8493) /// #calc.trunc(15.9) /// ``` #[func(title = "Truncate")] pub fn trunc( /// The number to truncate. value: DecNum, -) -> DecNum { +) -> StrResult { match value { - DecNum::Int(n) => DecNum::Int(n), - DecNum::Float(n) => DecNum::Float(n.trunc()), - DecNum::Decimal(n) => DecNum::Decimal(n.trunc()), + DecNum::Int(n) => Ok(n), + DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.trunc()) + .map_err(|_| too_large())?), + DecNum::Decimal(n) => Ok(i64::try_from(n.trunc()).map_err(|_| too_large())?), } } @@ -1006,6 +1000,10 @@ pub fn rem_euclid( /// Calculates the quotient (floored division) of two numbers. /// +/// Note that this function will always return an [integer]($int), and will +/// error if the resulting [`float`] or [`decimal`] is larger than the maximum +/// 64-bit signed integer or smaller than the minimum for that type. +/// /// ```example /// $ "quo"(a, b) &= floor(a/b) \ /// "quo"(14, 5) &= #calc.quo(14, 5) \ @@ -1019,7 +1017,7 @@ pub fn quo( dividend: DecNum, /// The divisor of the quotient. divisor: Spanned, -) -> SourceResult { +) -> SourceResult { if divisor.v.is_zero() { bail!(divisor.span, "divisor must not be zero"); } @@ -1036,7 +1034,7 @@ pub fn quo( .ok_or_else(too_large) .at(span)?; - Ok(floor(divided)) + floor(divided).at(span) } /// A value which can be passed to functions that work with integers and floats. diff --git a/crates/typst/src/foundations/int.rs b/crates/typst/src/foundations/int.rs index 1f29c2a61..2832be91a 100644 --- a/crates/typst/src/foundations/int.rs +++ b/crates/typst/src/foundations/int.rs @@ -368,7 +368,7 @@ cast! { v: Str => Self(parse_int(&v).map_err(|_| eco_format!("invalid integer: {}", v))?), } -fn convert_float_to_int(f: f64) -> StrResult { +pub fn convert_float_to_int(f: f64) -> StrResult { if f <= i64::MIN as f64 - 1.0 || f >= i64::MAX as f64 + 1.0 { Err(eco_format!("number too large")) } else { diff --git a/tests/suite/foundations/calc.typ b/tests/suite/foundations/calc.typ index 18cfa484c..4cf7ab2a4 100644 --- a/tests/suite/foundations/calc.typ +++ b/tests/suite/foundations/calc.typ @@ -122,10 +122,10 @@ #test(calc.quo(1, 1), 1) #test(calc.quo(5, 3), 1) #test(calc.quo(5, -3), -1) -#test(calc.quo(22.5, 10), 2.0) -#test(calc.quo(9, 4.5), 2.0) -#test(calc.quo(decimal("22.5"), 10), decimal("2")) -#test(calc.quo(decimal("9"), decimal("4.5")), decimal("2")) +#test(calc.quo(22.5, 10), 2) +#test(calc.quo(9, 4.5), 2) +#test(calc.quo(decimal("22.5"), 10), 2) +#test(calc.quo(decimal("9"), decimal("4.5")), 2) --- calc-quo-divisor-zero-1 --- // Error: 14-15 divisor must not be zero @@ -307,29 +307,29 @@ // Error: 2-41 the result is too large #calc.lcm(15486487489457, 4874879896543) ---- calc-rounding-larger-than-max-int --- +--- calc-round-larger-than-max-int --- #test(calc.round(decimal("9223372036854775809.5")), decimal("9223372036854775810")) #test(calc.round(9223372036854775809.5), 9223372036854775810.0) -#test(calc.floor(decimal("9223372036854775809.5")), decimal("9223372036854775809")) -#test(calc.floor(9223372036854775809.5), 9223372036854775809.0) -#test(calc.ceil(decimal("9223372036854775809.5")), decimal("9223372036854775810")) -#test(calc.ceil(9223372036854775809.5), 9223372036854775810.0) -#test(calc.trunc(decimal("9223372036854775809.5")), decimal("9223372036854775809")) -#test(calc.trunc(9223372036854775809.5), 9223372036854775809.0) -#test(calc.quo(decimal("9223372036854775809.5"), 1), decimal("9223372036854775809")) -#test(calc.quo(9223372036854775809.5, 1), 9223372036854775809.0) ---- calc-rounding-smaller-than-min-int --- +--- calc-floor-float-larger-than-max-int --- +// Error: 2-35 the result is too large +#calc.floor(9223372036854775809.5) + +--- calc-floor-decimal-larger-than-max-int --- +// Error: 2-46 the result is too large +#calc.floor(decimal("9223372036854775809.5")) + +--- calc-round-smaller-than-min-int --- #test(calc.round(decimal("-9223372036854775809.5")), decimal("-9223372036854775810")) #test(calc.round(-9223372036854775809.5), -9223372036854775810.0) -#test(calc.floor(decimal("-9223372036854775809.5")), decimal("-9223372036854775810")) -#test(calc.floor(-9223372036854775809.5), -9223372036854775810.0) -#test(calc.ceil(decimal("-9223372036854775809.5")), decimal("-9223372036854775809")) -#test(calc.ceil(-9223372036854775809.5), -9223372036854775809.0) -#test(calc.trunc(decimal("-9223372036854775809.5")), decimal("-9223372036854775809")) -#test(calc.trunc(-9223372036854775809.5), -9223372036854775809.0) -#test(calc.quo(decimal("-9223372036854775809.5"), 1), decimal("-9223372036854775810")) -#test(calc.quo(-9223372036854775809.5, 1), -9223372036854775810.0) + +--- calc-floor-float-smaller-than-min-int --- +// Error: 2-36 the result is too large +#calc.floor(-9223372036854775809.5) + +--- calc-floor-decimal-smaller-than-min-int --- +// Error: 2-47 the result is too large +#calc.floor(decimal("-9223372036854775809.5")) --- calc-min-nothing --- // Error: 2-12 expected at least one value