Zastanawiałem się, czy wartości 1/256, 2/256, 3/256, ... 254/256 i 255/256 są dokładnie reprezentowane jako f32. Teraz ktoś inteligentny pomyślałby o tym, jak działają i dowiedz się w ten sposób. Ale chciałbym sprawdzić to w programie. Wszystkie numery, które chcę sprawdzić, to frakcje i kontroluję wartości (tj. Brak wejścia użytkownika).

Zacząłem od tego:

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

Ale co? Próbowałem wydrukować numer, aby sprawdzić, czy wystąpiła duża liczba cyfr, ale to nie zawsze działa. Na przykład 0,4, który nie jest dokładnie reprezentowany:

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

Tutaj musimy ręcznie zwiększać precyzję, aby zobaczyć problemy. W każdym razie, patrząc na wyjście łańcuchowe, wydaje się i tak wyglądać na roztwór podziemny.

Najpierw pomyślałem, że może istnieć metoda na f32, ale to nie miałoby to znaczenia, prawda? Ponieważ kiedy już istnieje f32, nie ma sposobu, aby wiedzieć, czy jego wartość była zamierzona, czy nie. Więc w jakiś sposób musieliśmy dowiedzieć się podczas tworzenia wartości pływakowej i porównać do wartości "wyidealizowanej"?

Czy jest jakiś sposób sprawdzenia, czy wartość może być dokładnie reprezentowana jako f32?

6
Lukas Kalbertodt 20 luty 2019, 12:02

2 odpowiedzi

Najlepsza odpowiedź

Typ {x0}} z rug Crate może dokładnie reprezentować frakcje. Wykonamy również PartialEq<f32>, dzięki czemu można porównać dokładną reprezentację z f32 bezpośrednio, aby sprawdzić, czy są równe.

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

I jak widać z wyjścia, liczby, które chcesz przetestować, są rzeczywiście dokładnie reprezentowane jako f32.

Aby uzyskać więcej ciekawej wyjścia, spróbuj 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);
}

Pokazuje to, że tylko frakcje o mianowniku mocy-2 są dokładnie reprezentowane.

4
Lukas Kalbertodt 20 luty 2019, 11:03

Wykonaj obliczenia, które chcesz z wyższą precyzją ({X0}} jest oczywiste i najszybsze, ale są alternatywy: np. f128 BigDecimal , rug 's {x4}} lub { {X5}}, itd.), a następnie sprawdź to Wynik jest równy samemu przeliczeniu na f32 iz powrotem.

Zakładając f64 ze względu na przykład

d.is_finite() && (d as f32) as f64 == d

Oczywiście wynik tych obliczeń może trafić dokładnie jako f32, nawet jeśli dokładny wynik nie byłby, jak wskazuje komentarz jojonetki. Więc datatype, który chcesz, zależy od obliczeń. Na przykład. dla

1/256, 2/256, 3/256, ... 254/256 i 255/256

{{X0}.

0
Alexey Romanov 20 luty 2019, 09:48