How to identify floating point precision at runtime with Rust? - rust

How to identify that the precision of 1.0005 in the below code (rust playground link) is 4 at runtime?:
fn round(n: f64, precision: u32) -> f64 {
(n * 10_u32.pow(precision) as f64).round() / 10_i32.pow(precision) as f64
}
fn main() {
let x = 1.0005_f64;
println!("{:?}", round(x, 1));
println!("{:?}", round(x, 2));
println!("{:?}", round(x, 3));
println!("{:?}", round(x, 4));
println!("{:?}", round(x, 5));
}

I'm not sure whether I understand the question correctly. You want the number of decimal places?
fn round(n: f64, precision: u32) -> f64 {
(n * 10_u32.pow(precision) as f64).round() / 10_i32.pow(precision) as f64
}
fn precision(x: f64) -> Option<u32> {
for digits in 0..std::f64::DIGITS {
if round(x, digits) == x {
return Some(digits);
}
}
None
}
fn main() {
let x = 1.0005_f64;
println!("{:?}", precision(x));
}
Playground
I'd also recommend making the types in your round function a bit larger, so you don't run into overflow so fast. The above code fails already as x = 1e-10.
fn round(n: f64, precision: u32) -> f64 {
let precision = precision as f64;
(n * 10_f64.powf(precision)).round() / 10_f64.powf(precision)
}

Related

Rust: Using structs that contain a f32 field in a hashmap [duplicate]

I want to use a HashMap<f64, f64>, for saving the distances of a point with known x and key y to another point. f64 as value shouldn't matter here, the focus should be on key.
let mut map = HashMap<f64, f64>::new();
map.insert(0.4, f64::hypot(4.2, 50.0));
map.insert(1.8, f64::hypot(2.6, 50.0));
...
let a = map.get(&0.4).unwrap();
As f64 is neither Eq nor Hash, but only PartialEq, f64 is not sufficient as a key. I need to save the distances first, but also access the distances later by y. The type of y needs to be floating point precision, but if doesn't work with f64, I'll use an i64 with an known exponent.
I tried some hacks by using my own struct Dimension(f64) and then implementing Hash by converting the float into a String and then hashing it.
#[derive(PartialEq, Eq)]
struct DimensionKey(f64);
impl Hash for DimensionKey {
fn hash<H: Hasher>(&self, state: &mut H) {
format!("{}", self.0).hash(state);
}
}
It seems very bad and both solutions, my own struct or float as integers with base and exponent seem to be pretty complicated for just a key.
Update:
I can guarantee that my key never will be NaN, or an infinite value. Also, I won't calculate my keys, only iterating over them and using them. So there should no error with the known error with 0.1 + 0.2 ≠ 0.3.
How to do a binary search on a Vec of floats? and this question have in common to implement total ordering and equality for a floating number, the difference lies only in the hashing or iterating.
Presented with no comment beyond read all the other comments and answers to understand why you probably don't want to do this:
use std::{collections::HashMap, hash};
#[derive(Debug, Copy, Clone)]
struct DontUseThisUnlessYouUnderstandTheDangers(f64);
impl DontUseThisUnlessYouUnderstandTheDangers {
fn key(&self) -> u64 {
self.0.to_bits()
}
}
impl hash::Hash for DontUseThisUnlessYouUnderstandTheDangers {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.key().hash(state)
}
}
impl PartialEq for DontUseThisUnlessYouUnderstandTheDangers {
fn eq(&self, other: &DontUseThisUnlessYouUnderstandTheDangers) -> bool {
self.key() == other.key()
}
}
impl Eq for DontUseThisUnlessYouUnderstandTheDangers {}
fn main() {
let a = DontUseThisUnlessYouUnderstandTheDangers(0.1);
let b = DontUseThisUnlessYouUnderstandTheDangers(0.2);
let c = DontUseThisUnlessYouUnderstandTheDangers(0.3);
let mut map = HashMap::new();
map.insert(a, 1);
map.insert(b, 2);
println!("{:?}", map.get(&a));
println!("{:?}", map.get(&b));
println!("{:?}", map.get(&c));
}
Basically, if you want to treat a f64 as a set of bits that have no meaning, well, we can treat them as an equivalently sized bag of bits that know how to be hashed and bitwise-compared.
Don't be surprised when one of the 16 million NaN values doesn't equal another one.
You could split the f64 into the integral and fractional part and store them in a struct in the following manner:
#[derive(Hash, Eq, PartialEq)]
struct Distance {
integral: u64,
fractional: u64
}
The rest is straightforward:
use std::collections::HashMap;
#[derive(Hash, Eq, PartialEq)]
struct Distance {
integral: u64,
fractional: u64
}
impl Distance {
fn new(i: u64, f: u64) -> Distance {
Distance {
integral: i,
fractional: f
}
}
}
fn main() {
let mut map: HashMap<Distance, f64> = HashMap::new();
map.insert(Distance::new(0, 4), f64::hypot(4.2, 50.0));
map.insert(Distance::new(1, 8), f64::hypot(2.6, 50.0));
assert_eq!(map.get(&Distance::new(0, 4)), Some(&f64::hypot(4.2, 50.0)));
}
Edit: As Veedrac said, a more general and efficient option would be to deconstruct the f64 into a mantissa-exponent-sign triplet. The function that can do this, integer_decode(), is deprecated in std, but it can be easily found in Rust GitHub.
The integer_decode() function can be defined as follows:
use std::mem;
fn integer_decode(val: f64) -> (u64, i16, i8) {
let bits: u64 = unsafe { mem::transmute(val) };
let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
let mantissa = if exponent == 0 {
(bits & 0xfffffffffffff) << 1
} else {
(bits & 0xfffffffffffff) | 0x10000000000000
};
exponent -= 1023 + 52;
(mantissa, exponent, sign)
}
The definition of Distance could then be:
#[derive(Hash, Eq, PartialEq)]
struct Distance((u64, i16, i8));
impl Distance {
fn new(val: f64) -> Distance {
Distance(integer_decode(val))
}
}
This variant is also easier to use:
fn main() {
let mut map: HashMap<Distance, f64> = HashMap::new();
map.insert(Distance::new(0.4), f64::hypot(4.2, 50.0));
map.insert(Distance::new(1.8), f64::hypot(2.6, 50.0));
assert_eq!(map.get(&Distance::new(0.4)), Some(&f64::hypot(4.2, 50.0)));
}
Unfortunately, floating types equality is hard and counter-intuitive:
fn main() {
println!("{} {} {}", 0.1 + 0.2, 0.3, 0.1 + 0.2 == 0.3);
}
// Prints: 0.30000000000000004 0.3 false
And therefore hashing is hard too, since hashes of equal values should be equal.
If, in your case, you have a small enough range to fit your number in a i64 and you can accept the loss of precision, then a simple solution is to canonicalize first and then define equal/hash in terms of the canonical value:
use std::cmp::Eq;
#[derive(Debug)]
struct Distance(f64);
impl Distance {
fn canonicalize(&self) -> i64 {
(self.0 * 1024.0 * 1024.0).round() as i64
}
}
impl PartialEq for Distance {
fn eq(&self, other: &Distance) -> bool {
self.canonicalize() == other.canonicalize()
}
}
impl Eq for Distance {}
fn main() {
let d = Distance(0.1 + 0.2);
let e = Distance(0.3);
println!("{:?} {:?} {:?}", d, e, d == e);
}
// Prints: Distance(0.30000000000000004) Distance(0.3) true
Hash just follows, and from then on you can use Distance as a key in the hash map:
impl Hash for Distance {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.canonicalize().hash(state);
}
}
fn main() {
let d = Distance(0.1 + 0.2);
let e = Distance(0.3);
let mut m = HashMap::new();
m.insert(d, "Hello");
println!("{:?}", m.get(&e));
}
// Prints: Some("Hello")
Warning: To reiterate, this strategy only works if (a) the dynamic range of values is small enough to be captured in a i64 (19 digits) and if (b) the dynamic range is known in advance as the factor is static. Fortunately, this holds for many common problems, but it is something to document and test...
You can use the ordered_float crate which does this for you.

How to make this Rust code less redundant?

How would you design this better in Rust? More Specifically is there a way to collapse the redundancy down using traits or enums?
Background: I have a C++ / Python background and this is my first attempt to see how the language actually flows after reading the Rust book. Not having class inheritance is something I don't really know how to design around yet.
trait TemperatureConversion {
// https://www.nist.gov/pml/weights-and-measures/si-units-temperature
fn to_celcius(&self) -> f64;
fn to_fahrenheit(&self) -> f64;
fn to_kelvin(&self) -> f64;
}
struct Celcius {
value: f64,
}
struct Fahrenheit {
value: f64,
}
struct Kelvin {
value: f64,
}
impl Celcius {
fn new(value: f64) -> Celcius {
Celcius { value }
}
}
impl Fahrenheit {
fn new(value: f64) -> Fahrenheit {
Fahrenheit { value }
}
}
impl Kelvin {
fn new(value: f64) -> Kelvin {
Kelvin { value }
}
}
impl TemperatureConversion for Celcius {
fn to_celcius(&self) -> f64 {
self.value
}
fn to_fahrenheit(&self) -> f64 {
(self.value * 1.8) + 32.0
}
fn to_kelvin(&self) -> f64 {
self.value + 273.15
}
}
impl TemperatureConversion for Fahrenheit {
fn to_celcius(&self) -> f64 {
(self.value - 32.0) / 1.8
}
fn to_fahrenheit(&self) -> f64 {
self.value
}
fn to_kelvin(&self) -> f64 {
(self.value - 32.0) / 1.8 + 273.15
}
}
impl TemperatureConversion for Kelvin {
fn to_celcius(&self) -> f64 {
self.value - 273.15
}
fn to_fahrenheit(&self) -> f64 {
(self.value - 273.15) * 1.8 + 32.0
}
fn to_kelvin(&self) -> f64 {
self.value
}
}
fn main() {
let c = Celcius::new(100.0);
println!("100C = {:.2}F or {:.2}K", c.to_fahrenheit(), c.to_kelvin());
let f = Fahrenheit::new(100.0);
println!("100F = {:.2}C or {:.2}K", f.to_celcius(), f.to_kelvin());
let k = Kelvin::new(100.0);
println!("100K = {:.2}C or {:.2}F", k.to_celcius(), k.to_fahrenheit());
}
edit: I believe this is the fix:
struct KelvinTemperature {
kelvin: f64,
}
impl KelvinTemperature {
fn new(kelvin: f64) -> KelvinTemperature {
KelvinTemperature { kelvin }
}
fn from_celcius(value: f64) -> KelvinTemperature {
KelvinTemperature {
kelvin: value + 273.15,
}
}
fn from_fahrenheit(value: f64) -> KelvinTemperature {
KelvinTemperature {
kelvin: (value - 32.0) / 1.8 + 273.15,
}
}
fn to_celcius(&self) -> f64 {
self.kelvin - 273.15
}
fn to_fahrenheit(&self) -> f64 {
(self.kelvin - 273.15) * 1.8 + 32.0
}
fn to_kelvin(&self) -> f64 {
self.kelvin
}
}
fn main() {
let temperature = KelvinTemperature::from_celcius(100.0);
println!(
"{:.2}C = {:.2}F = {:.2}K",
temperature.to_celcius(),
temperature.to_fahrenheit(),
temperature.to_kelvin()
);
}
The best design would likely be to avoid re-inventing the wheel and realize someone else has already done this better than we ever will in the time available. The uom (Units Of Measurement) crate provides units for almost every unit you can think of as well as every combination of them (Even composite units like K*ft^2/sec). However that does not make for a very helpful explanation so lets just ignore it for now.
The first issue I see with this code is that it isn't very easy to expand. If you want to add a new temperature you need to add to the TemperatureConversion trait and implement a bunch of functions for all of your conversion rates. The first change I would make would be to turn Temperature into a trait so it is easier to work with.
pub trait Temperature: Copy {
fn to_kelvin(self) -> f64;
fn from_kelvin(x: f64) -> Self;
/// Convert self to a different unit of temperature
fn convert<T: Temperature>(self) -> T {
T::from_kelvin(self.to_kelvin())
}
}
This also gives us the benefit of letting us use it to constrain type parameters later.
pub fn calculate_stuff<T: Temperature>(a: T, b: T) -> T;
Next, since we know that temperatures will all be implemented in the same way and there might be a bunch of them, it may be easier to make a macro for them.
macro_rules! define_temperature {
($name:ident, $kelvin_at_zero:literal, $kelvin_per_unit:literal) => {
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct $name(f64);
impl Temperature for $name {
fn to_kelvin(self) -> f64 {
self.0 * $kelvin_per_unit + $kelvin_at_zero
}
fn from_kelvin(x: f64) -> Self {
Self((x - $kelvin_at_zero) / $kelvin_per_unit)
}
}
};
}
define_temperature! {Kelvin, 1.0, 1.0}
define_temperature! {Celsius, 273.1, 1.0}
define_temperature! {Fahrenheit, 255.3722, 0.5555}
The macro makes it easy to implement a bunch of different units based on their conversion rates, but the trait is not too restrictive so we could potentially implement units that do not follow a linear scale.

Writing rust function with traits working for Vec and array []

I would like to implement a function in rust, computing the norm of an array or Vec
for an Vec<f64> I would write the function as
pub fn vector_norm( vec_a : &Vec<f64> ) -> f64 {
let mut norm = 0 as f64;
for i in 0..vec_a.len(){
norm += vec_a[i] * vec_a[i];
}
norm.sqrt()
}
and for an &[f64] I would do
pub fn vector_norm( vec_a : &[f64] ) -> f64 {
let mut norm = 0 as f64;
for i in 0..vec_a.len(){
norm += vec_a[i] * vec_a[i];
}
norm.sqrt()
}
But is there a way to combine both versions into a single function by the use of traits. I was thinking of something like
pub fn vector_norm<T:std::iter::ExactSizeIterator>
( vec_a : &T ) -> f64 {
let mut norm = 0 as f64;
for i in 0..vec_a.len(){
norm += vec_a[i] * vec_a[i];
}
norm.sqrt()
}
This does not work because the the template parameter T is not indexable. Is it possible to do this somehow?? Maybe with an iterator trait or something?
First of all, Vec<T> implements Deref for [T]. This means that &Vec<f64> can be implicitly converted into &[f64]. So, just taking in a &[f64] will work:
fn vector_norm(vec_a: &[f64]) -> f64 {
let mut norm = 0 as f64;
for i in 0..vec_a.len() {
norm += vec_a[i] * vec_a[i];
}
norm.sqrt()
}
fn main() {
let my_vec = vec![1.0, 2.0, 3.0];
// &my_vec is implicitly converted to &[f64]
println!("{:?}", vector_norm(&my_vec));
}
However, if you want to broaden the acceptable values even further to all slice-like types, perhaps AsRef may be of use:
fn vector_norm<T: AsRef<[f64]>>(vec_a: T) -> f64 {
// use AsRef to get a &[f64]
let vec_a: &[f64] = vec_a.as_ref();
let mut norm = 0 as f64;
for i in 0..vec_a.len() {
norm += vec_a[i] * vec_a[i];
}
norm.sqrt()
}
fn main() {
let my_vec = vec![1.0, 2.0, 3.0];
println!("{:?}", vector_norm(&my_vec));
}
In addition to Aplet's answer, I'd add that if you're taking something that is only going to be used in a for _ in loop, you might want to look at IntoIterator.
fn vector_norm<T: IntoIterator<Item = f64>>(t: T) -> f64 {
let mut norm = 0f64;
for i in t {
norm += i * i;
}
norm.sqrt()
}
When you write for i in t, the compiler rewrites that into something that looks a bit more like this:
let mut iter = t.into_iter();
loop {
match iter.next() {
None => break,
Some(i) => {
// loop body
}
}
}
So if you only want to constrain your input as "something that works in a for loop", IntoIterator is the trait you're looking for.

How to fix rust operations not working as expected?

I have implemented a simple command-line calculator in Rust. The add function acts as normal but the subtract, multiply, and divide functions don't work. The rest of the code is on GitHub: https://github.com/henryboisdequin/rust-calculator.
calc.rs
impl Calc {
pub fn add(arr: Vec<i64>) -> f64 {
let mut total: f64 = 0.0;
for num in arr {
total += num as f64;
}
total
}
pub fn sub(arr: Vec<i64>) -> f64 {
let mut total: f64 = 0.0;
for num in arr {
total -= num as f64;
}
total
}
pub fn mul(arr: Vec<i64>) -> f64 {
let mut total: f64 = 0.0;
for num in arr {
total *= num as f64;
}
total
}
pub fn div(arr: Vec<i64>) -> f64 {
let mut total: f64 = 0.0;
for num in arr {
total /= num as f64;
}
total
}
}
Instead of having your functions take Vec<i64>, I would instead suggest &[i64], or even &[f64] to avoid the as f64. This wouldn't really break your existing code, as you can just borrow a Vec<i64>, to have it auto dereference into &[i64].
You can simplify add() by using sum(), and mul() by using product().
pub fn add(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).sum()
}
pub fn mul(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).product()
}
You can similarly simplify sub() and div() with next() and then fold().
pub fn sub(arr: &[i64]) -> f64 {
let mut it = arr.iter().map(|&x| x as f64);
it.next()
.map(|x| it.fold(x, |acc, x| acc - x))
.unwrap_or(0.0)
}
pub fn div(arr: &[i64]) -> f64 {
let mut it = arr.iter().map(|&x| x as f64);
it.next()
.map(|x| it.fold(x, |acc, x| acc / x))
.unwrap_or(0.0)
}
You can even simplify them further, by using fold_first(). However that is currently experimental and nightly only. Instead you can use fold1() from the itertools crate, or reduce() from the reduce crate.
// itertools = "0.10"
use itertools::Itertools;
pub fn sub(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).fold1(|a, b| a - b).unwrap_or(0.0)
}
pub fn div(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).fold1(|a, b| a / b).unwrap_or(0.0)
}
You can even replace the closures with Sub::sub and Div::div.
// itertools = "0.10"
use itertools::Itertools;
use std::ops::{Div, Sub};
pub fn sub(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).fold1(Sub::sub).unwrap_or(0.0)
}
pub fn div(arr: &[i64]) -> f64 {
arr.iter().map(|&x| x as f64).fold1(Div::div).unwrap_or(0.0)
}
Siguza helped me fix this problem by specifying that my addition function only works because addition is commutative but the other operations are failing because they are not.
Here is the right code:
pub struct Calc;
impl Calc {
pub fn add(arr: Vec<i64>) -> f64 {
let mut total: f64 = 0.0;
for num in arr {
total += num as f64;
}
total
}
pub fn sub(arr: Vec<i64>) -> f64 {
let mut total: f64 = arr[0] as f64;
let mut counter = 0;
while counter != arr.len() - 1 {
total -= arr[counter + 1] as f64;
counter += 1;
}
total
}
pub fn mul(arr: Vec<i64>) -> f64 {
let mut total: f64 = arr[0] as f64;
let mut counter = 0;
while counter != arr.len() - 1 {
total *= arr[counter + 1] as f64;
counter += 1;
}
total
}
pub fn div(arr: Vec<i64>) -> f64 {
let mut total: f64 = arr[0] as f64;
let mut counter = 0;
while counter != arr.len() - 1 {
total /= arr[counter + 1] as f64;
counter += 1;
}
total
}
}
For the operations excluding 0, instead of assigning the total to 0.0, I assigned the total to the first element of the given array and -/*// the total with the rest of the elements in the array.

How can I convert a f64 to f32 and get the closest approximation and the next greater or smaller value?

Possible pseudocode for the operation could be:
fn f32_greater(x: f64) -> f32 {
let mut y = x as f32; //I get closest
while f64::from(y) < x {
y = nextafter(y, f32::INFINITY);
}
y
}
fn f32_smaller(x: f64) -> f32 {
let mut y = x as f32; //I get closest
while f64::from(y) > x {
y = nextafter(y, f32::NEG_INFINITY);
}
y
}
I can not find an equivalent to C11's nextafter function in the libc crate or in the methods on f64
For context, I have an R-tree index using f32. I want to search the region with coordinates provided as a f64, so I need the smallest possible region in f32 that includes the f64 value.
This function was removed from the standard library. A solution could be to use the float_extras crate, but I don't really like the way of this crate so here my solution:
mod float {
use libc::{c_double, c_float};
use std::{f32, f64};
#[link_name = "m"]
extern "C" {
pub fn nextafter(x: c_double, y: c_double) -> c_double;
pub fn nextafterf(x: c_float, y: c_float) -> c_float;
// long double nextafterl(long double x, long double y);
// double nexttoward(double x, long double y);
// float nexttowardf(float x, long double y);
// long double nexttowardl(long double x, long double y);
}
pub trait NextAfter {
fn next_after(self, y: Self) -> Self;
}
impl NextAfter for f32 {
fn next_after(self, y: Self) -> Self {
unsafe { nextafterf(self, y) }
}
}
impl NextAfter for f64 {
fn next_after(self, y: Self) -> Self {
unsafe { nextafter(self, y) }
}
}
pub trait Succ {
fn succ(self) -> Self;
}
impl Succ for f32 {
fn succ(self) -> Self {
self.next_after(f32::INFINITY)
}
}
impl Succ for f64 {
fn succ(self) -> Self {
self.next_after(f64::INFINITY)
}
}
pub trait Pred {
fn pred(self) -> Self;
}
impl Pred for f32 {
fn pred(self) -> Self {
self.next_after(f32::NEG_INFINITY)
}
}
impl Pred for f64 {
fn pred(self) -> Self {
self.next_after(f64::NEG_INFINITY)
}
}
}
use crate::float::{Pred, Succ};
use num_traits::cast::{FromPrimitive, ToPrimitive};
fn f32_greater<T>(x: T) -> Option<f32>
where
T: ToPrimitive + FromPrimitive + std::cmp::PartialOrd,
{
let mut y = x.to_f32()?;
while T::from_f32(y)? < x {
y = y.succ();
}
Some(y)
}
fn f32_smaller<T>(x: T) -> Option<f32>
where
T: ToPrimitive + FromPrimitive + std::cmp::PartialOrd,
{
let mut y = x.to_f32()?;
while T::from_f32(y)? > x {
y = y.pred();
}
Some(y)
}
fn main() {
let a = 42.4242424242424242;
println!(
"{:.16?} < {:.16} < {:.16?}",
f32_smaller(a),
a,
f32_greater(a)
);
}
I don't understand why they don't include it in the num crate.

Resources