Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a number is exactly representable as `f32`

I wondered whether the values 1/256, 2/256, 3/256, ... 254/256 and 255/256 are exactly representable as f32. Now, someone smart would think about how floating point numbers work and find out that way. But I would like to check that in a program. All numbers I want to check are fractions and I control the values (i.e. no user input).

I started with this:

for n in 1u8..=255 {
    let f = (n as f32) / 256.0;
    println!("{}", f);
}

But now what? I tried printing the number to see if there were a large number of recurring digits, but that doesn't always work. For example, 0.4 which is not exactly representable:

println!("{}", 0.4);     // prints "0.4"
println!("{:.20}", 0.4); // prints "0.40000000000000002220"

Here we have to manually crank up the precision to see the problems. And in any case, looking at the string output seems like a suboptimal solution anyway.

First I thought that there might be a method on f32, but this wouldn't make much sense, would it? Because when a f32 already exists, there is no way to know if its value was intended or not. So we somehow have to find out when creating the float value and compare to the "idealized" value?

Is there any way to check if a value can be exactly represented as f32?

like image 819
Lukas Kalbertodt Avatar asked Feb 20 '19 09:02

Lukas Kalbertodt


People also ask

What are the special values of F32?

Additionally, f32 can represent some special values: −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a possible value. For comparison −0.0 = +0.0, but floating point operations can carry the sign bit through arithmetic operations.

What is a 32 bit floating point number?

A 32-bit floating point type (specifically, the “binary32” type defined in IEEE 754-2008). This type can represent a wide range of decimal numbers, like 3.5, 27 , -113.75, 0.0078125, 34359738368, 0, -1.

Can N be represented as the sum of two numbers?

Therefore, the required answer is Yes. Approach: The idea is to store the perfect cubes of all numbers from 1 to cubic root of N in a Map and check if N can be represented as the sum of two numbers present in the Map or not. Follow the steps below to solve the problem:

What is the difference between floats and real numbers?

However, being able to represent this wide range of numbers comes at the cost of precision: floats can only represent some of the real numbers and calculation with floats round to a nearby representable number.


1 Answers

The type Rational from the rug crate can represent fractions exactly. It also implements PartialEq<f32> so you can compare the exact representation with your f32 directly to check if they are equal.

for n in 1u8..=255u8 {
    let rat = Rational::from((n, 256));
    let f = (n as f32) / 256.0;
    println!("{}/256 -> {}", n, rat == f);
}

And as you can see from the output, the numbers you want to test are indeed exactly representable as f32.

To get more a more interesting output, try 1 / n:

for n in 1u8..=255u8 {
    let rat = Rational::from((1, n));
    let f = 1.0 / (n as f32);
    println!("1/{} -> {}", n, rat == f);
}

This shows that only fractions with a power-of-2 denominator are exactly representable.

like image 93
Lukas Kalbertodt Avatar answered Sep 22 '22 06:09

Lukas Kalbertodt