Podział liczb całkowitych: Jak produkujesz podwójne?


249

Dla tego bloku kodu:

int num = 5;
int denom = 7;
double d = num / denom;

wartość dwynosi 0.0. Można go zmusić do pracy, przesyłając:

double d = ((double) num) / denom;

Ale czy jest inny sposób na uzyskanie prawidłowego doublewyniku? Nie lubię rzucać prymitywów, którzy wiedzą, co może się zdarzyć.



9
rzutowanie „int” na double jest bezpieczne, zawsze otrzymasz tę samą wartość bez utraty precyzji.
Peter Lawrey,

1
Chciałbym wiedzieć, czy następujące kroki są prawidłowe dla kompilatora, podejmowane przez: 1) cast num do float 2) cast denom do float oraz 2) divide num by denom. Daj mi znać, jeśli się mylę.
mannyee

Odpowiedzi:


156
double num = 5;

To pozwala uniknąć obsady. Przekonasz się jednak, że konwersje obsady są dobrze zdefiniowane. Nie musisz zgadywać, po prostu sprawdź JLS . int to double jest rozszerzającą konwersją. Od pkt 5.1.2 :

Poszerzenie prymitywnych konwersji nie powoduje utraty informacji o ogólnej wielkości wartości liczbowej.

[...]

Konwersja wartości int lub długiej na zmiennoprzecinkową lub długiej wartości na podwójną może spowodować utratę precyzji - to znaczy, że wynik może utracić niektóre z najmniej znaczących bitów wartości. W takim przypadku wynikowa liczba zmiennoprzecinkowa będzie poprawnie zaokrągloną wersją wartości całkowitej, przy użyciu trybu zaokrąglania do najbliższego IEEE 754 (§ 4.2.2) .

5 można wyrazić dokładnie jako podwójne.


60
+1. Przestań bać się castingu. Dowiedz się, jak to działa. Wszystko jest dobrze zdefiniowane.
Mark Peters

1
@FabricioPH Działa to w każdej sytuacji i identycznie jak w rozwiązaniu * 1.0.
Witruwiusz

3
@Saposhiente To wykracza poza działanie lub nie. Zmiana typu zmiennej wydaje mi się brudnym kodem. Jeśli masz coś, co MUSI BYĆ liczbą całkowitą i zmienisz reprezentację dla liczby zmiennoprzecinkowej tylko po to, aby móc wykonać operację matematyczną, możesz ryzykować sam. Również w pewnym kontekście odczytanie zmiennej jako liczby całkowitej ułatwia zrozumienie kodu.
Fabricio PH

2
@FabricioPH, pomnożenie przez 1.0nadal zmienia typ wyniku, tylko w inny sposób. „Wydaje mi się nieczytelnym kodem” nie wyjaśnia, w którym scenariuszu (-ach) Twoim zdaniem pomnożenie 1.0jest w rzeczywistości lepsze (lub dlaczego tak może być).
Matthew Flaschen

4
@FabricioPH Ty i OP wydajecie się pracować w fałszywym przekonaniu (gdzie mówicie „Zmiana typu zmiennej”), które w (double)numjakiś sposób się zmieniają num. Nie robi - tworzy tymczasowe podwójne dla kontekstu instrukcji.
Paul Tomblin

100

Co jest złego w rzucaniu prymitywów?

Jeśli z jakiegoś powodu nie chcesz castować, możesz to zrobić

double d = num * 1.0 / denom;

63
... która wykonuje ukrytą obsadę przed rozmnożeniem
Alice Purcell

2
W większości przypadków jest to lepsze niż zmiana typu innej zmiennej.
Fabricio PH,

3
@FabricioPH Nic nie sugeruje, że jest to prawdą, chyba że masz źródło, na które można cytować.
Witruwiusz

1
@Saposhiente odpowiedział w innym podobnym komentarzu
Fabricio PH

1
Możesz także użyć postfiksu „D” (aby wykonać ten sam niejawny rzut); - więc na przykład:double d = num * 1D / denom;
BrainSlugs83

73

Nie lubię rzucać prymitywów, którzy wiedzą, co może się zdarzyć.

Dlaczego masz irracjonalny lęk przed rzucaniem prymitywów? Nic złego się stanie, gdy rzucił intsię double. Jeśli nie masz pewności, jak to działa, poszukaj go w specyfikacji języka Java . Oddające intTO doublejest poszerzenie prymitywny konwersji .

Możesz pozbyć się dodatkowej pary nawiasów, rzucając mianownik zamiast licznika:

double d = num / (double) denom;

2
równie dobrze możesz zrobić double d = (double) num / denom;... (OK, ten zależy od pierwszeństwa)
użytkownik85421

27

Jeśli zmienisz typ zmiennej, musisz pamiętać, aby ponownie podkraść się dwukrotnie, jeśli zmieni się formuła, ponieważ jeśli ta zmienna przestanie być częścią obliczeń, wynik zostanie pomieszany. Przyzwyczajam się do rzucania w obliczeniach i dodam komentarz obok niego.

double d = 5 / (double) 20; //cast to double, to do floating point calculations

Pamiętaj, że rzutowanie wyniku tego nie zrobi

double d = (double)(5 / 20); //produces 0.0

17

Rzuć jedną z liczb całkowitych / obu liczb całkowitych, aby uzyskać liczbę zmiennoprzecinkową, aby wymusić wykonanie operacji za pomocą matematyki zmiennoprzecinkowej. W przeciwnym razie zawsze preferowana jest matematyka całkowita. Więc:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

Pamiętaj, że rzutowanie wyniku tego nie zrobi. Ponieważ pierwszy podział odbywa się zgodnie z regułą pierwszeństwa.

double d = (double)(5 / 20); //produces 0.0

Nie sądzę, że jest jakiś problem z castingiem, o którym myślisz.


10

Type Casting to jedyny sposób


Generowanie doublez dzielenia liczb całkowitych - nie ma innej drogi bez rzutowania (być może nie zrobisz tego wprost, ale tak się stanie).

Istnieje kilka sposobów na uzyskanie dokładnej doublewartości (gdzie numi gdzie denomjest inttyp, i oczywiście z rzutowaniem ) -

  1. z jawnym rzutowaniem:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

ale nie double d = (double) (num / denom);

  1. z niejawnym rzutowaniem:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

ale nie double d = num / denom * 1.0;
i niedouble d = 0.0 + ( num / denom );


2

użyj czegoś takiego jak:

double step = 1d / 5;

(1d jest rzutowany na podwójny)


5
1d nie jest obsadą, to tylko deklaracja.
Sefier Tang

0

Możesz rozważyć zawinięcie operacji. Na przykład:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

Umożliwia to sprawdzenie (tylko raz), czy obsada działa dokładnie tak, jak chcesz. Ta metoda może również podlegać testom, aby upewnić się, że nadal wykonuje to, co chcesz. Nie ma również znaczenia, jakiej sztuczki użyjesz, aby spowodować podział (możesz użyć jednej z odpowiedzi tutaj), o ile spowoduje to poprawny wynik. Gdziekolwiek musisz podzielić dwie liczby całkowite, możesz teraz zadzwonić Utils::dividei zaufać, że zrobi to dobrze.



Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.