From 21960902f730712f693abfb08370cc938c4f6567 Mon Sep 17 00:00:00 2001 From: David Peter Date: Fri, 11 Oct 2024 13:41:12 +0200 Subject: [PATCH] Fix unit_of-related crashes This is a hotfix for #521. It doesn't solve the underlying issue, but it prevents Numbat from crashing, throwing a runtime error instead. --- book/src/list-functions-other.md | 20 ++++++++++---------- examples/tests/core.nbt | 2 -- examples/tests/numerics.nbt | 4 +++- numbat/modules/core/quantities.nbt | 12 +++++++----- numbat/src/ffi/functions.rs | 9 +++++---- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 179f4360..9d528e9b 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -89,36 +89,36 @@ fn is_finite(n: T) -> Bool Defined in: `core::quantities` -### `unit_of` -Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. +### `value_of` +Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. ```nbt -fn unit_of(x: T) -> T +fn value_of(x: T) -> Scalar ```
Examples -
>>> unit_of(20 km/h) +
>>> value_of(20 km/h) - = 1 km/h [Velocity] + = 20
-### `value_of` -Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. +### `unit_of` +Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. Returns an error if the quantity is zero. ```nbt -fn value_of(x: T) -> Scalar +fn unit_of(x: T) -> T ```
Examples -
>>> value_of(20 km/h) +
>>> unit_of(20 km/h) - = 20 + = 1 km/h [Velocity]
diff --git a/examples/tests/core.nbt b/examples/tests/core.nbt index d8332575..b6dff2fa 100644 --- a/examples/tests/core.nbt +++ b/examples/tests/core.nbt @@ -1,7 +1,5 @@ # unit_of -assert_eq(unit_of(0), 1) - assert_eq(unit_of(1), 1) assert_eq(unit_of(1.2345), 1) diff --git a/examples/tests/numerics.nbt b/examples/tests/numerics.nbt index bdbe450e..4f2a1268 100644 --- a/examples/tests/numerics.nbt +++ b/examples/tests/numerics.nbt @@ -20,7 +20,9 @@ assert_eq(fixed_point(f_sqrt3, 1, 1e-10), sqrt(3), 1e-10) # Differentiation assert_eq(diff(log, 2.0), 0.5, 1e-5) -assert_eq(diff(sin, 0.0), 1.0, 1e-5) + +# TODO: Sadly, the following is not possible at the moment. See https://github.com/sharkdp/numbat/issues/521 for details +# assert_eq(diff(sin, 0.0), 1.0, 1e-5) assert_eq(diff(sqrt, 1.0), 0.5, 1e-5) diff --git a/numbat/modules/core/quantities.nbt b/numbat/modules/core/quantities.nbt index 3d886bd2..d750e000 100644 --- a/numbat/modules/core/quantities.nbt +++ b/numbat/modules/core/quantities.nbt @@ -1,9 +1,11 @@ use core::scalar - -@description("Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.") -@example("unit_of(20 km/h)") -fn unit_of(x: T) -> T +use core::error @description("Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.") @example("value_of(20 km/h)") -fn value_of(x: T) -> Scalar = x / unit_of(x) +fn value_of(x: T) -> Scalar + +@description("Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. Returns an error if the quantity is zero.") +@example("unit_of(20 km/h)") +fn unit_of(x: T) -> T = if x_value == 0 then error("…") else x / value_of(x) + where x_value = value_of(x) diff --git a/numbat/src/ffi/functions.rs b/numbat/src/ffi/functions.rs index d2650762..a849d460 100644 --- a/numbat/src/ffi/functions.rs +++ b/numbat/src/ffi/functions.rs @@ -38,7 +38,7 @@ pub(crate) fn functions() -> &'static HashMap { // Core insert_function!(error, 1..=1); - insert_function!(unit_of, 1..=1); + insert_function!(value_of, 1..=1); // Math insert_function!("mod", mod_, 2..=2); @@ -118,7 +118,8 @@ fn error(mut args: Args) -> Result { Err(RuntimeError::UserError(arg!(args).unsafe_as_string())) } -fn unit_of(mut args: Args) -> Result { - let input_unit = quantity_arg!(args).unit().clone(); - return_quantity!(1.0, input_unit) +fn value_of(mut args: Args) -> Result { + let quantity = quantity_arg!(args); + + return_scalar!(quantity.unsafe_value().to_f64()) }