Tehforsch / diman

Define rust compile time unit systems using const generics
52 stars 2 forks source link

Unit ergonomics #73

Closed Tehforsch closed 6 months ago

Tehforsch commented 7 months ago

Solves #56 #64 #52 #33

This completely revamps how units work and how their magnitudes are stored. Finally gets rid of all the in_... functions and replaces them by a single value_in function and opens the door for round_in etc. in the future.

The readme got restructured and in the process I switched to using cargo-rdme to generate the readme from lib.rs which makes the process a lot nicer and goes a long way to solving #58 .

For future me: I went on a little bit of a detour while trying to solve the problem of how to represent the magnitude of a unit at compile time. Even if const_fn_float_arithmetic was stable, we arent allowed to use floats in const generics, so this approach is not an option right now.

For now, single units are represented with their magnitude decomposed into mantissa and exponent (which can be stored in a const generic) and then converted back to a float at runtime. This can be promoted to compile time once the corresponding features are stabilized.

I tried implementing multiplication and division using the mantissa/exponent representation, since this would make it possible to do this at compile time. However, even getting multiplication exactly right is very hard and division is virtually impossible. I don't know how exactly these algorithms are implemented on the CPU and at this point I also don't want to know anymore, but I am writing this down so that I don't attempt to do this anymore in the future.

Because of these limitations, at the moment an expression like meters * second is translated from two Units into a single RuntimeUnit in which the magnitude is stored at runtime. This means that, depending on compiler optimization, there could be some overhead in constructing a composite unit in an expression like (meters / second).new(50.0). I suspect that the compiler will optimize this away a lot of the times and also think that this is probably rarely performance-relevant. Nevertheless, I pointed the possibiliy out in the new readme and am waiting desperately for const supports for floats.

For the very far future, it could be nice to support arbitrary precision magnitudes using vector space magnitudes or some related concept. Currently, this is stopped by the strong limitations imposed by const fns. Even if one writes the code without requiring any allocations (by constructing a fixed size array of vector entries, for example) and avoids borrowing by painfully/carefully moving everywhere, one isnt even close to solving the problem, because even for loops are not allowed in const fns at the moment. So scratch that idea for the next couple of years.