Dlaczego podczas deklarowania wartości zmiennoprzecinkowych wymagane jest „f”?


87

Przykład:

float timeRemaining = 0.58f;

Dlaczego fna końcu tego numeru jest wymagana litera?


4
Prawdopodobnie, bo inaczej byłby traktowany jako double.
Uwe Keim,

2
0.58 (bez przyrostka f) jest literałem typu double i nie można przypisać wartości double do float, tak jak nie można przypisać wartości int do łańcucha. Możesz jednak przypisać wartość zmiennoprzecinkową do double, ponieważ tutaj rozszerzasz (C # niejawnie przekonwertuje to za Ciebie, ponieważ żadna precyzja nie zostanie utracona).
Dave New,


@AVD Chociaż jest to duplikat, powyższy tytuł pytania nie powoduje umieszczenia linku na liście możliwych duplikatów. Może to więc stanowić wartość dodaną przynajmniej pod względem większej liczby kryteriów wyszukiwania.
Adam Houldsworth,

1
@AdamHouldsworth - cała droga prowadzi double & int .
adatapost

Odpowiedzi:


97

Twoja deklaracja pływaka składa się z dwóch części:

  1. Deklaruje, że zmienna timeRemainingjest typu float.
  2. Przypisuje wartość 0.58tej zmiennej.

Problem pojawia się w części 2.

Prawa strona jest oceniana samodzielnie. Zgodnie ze specyfikacją C # liczba zawierająca kropkę dziesiętną, która nie ma sufiksu, jest interpretowana jako double.

Mamy więc teraz doublewartość, którą chcemy przypisać zmiennej typu float. Aby to zrobić, musi istnieć niejawna konwersja z doublena float. Nie ma takiej konwersji, ponieważ możesz (iw tym przypadku tracisz) informacje w konwersji.

Powodem jest to, że wartość używana przez kompilator nie jest tak naprawdę 0,58, ale wartością zmiennoprzecinkową najbliższą 0,58, czyli 0,57999999999999978655962351581366 ... dla doublei dokładnie 0,579999946057796478271484375 dla float.

Ściśle mówiąc, fnie jest to wymagane. Możesz uniknąć używania fprzyrostka, rzutując wartość na float:

float timeRemaining = (float)0.58;

4
Dwa pytania, jak doszedłeś do numeru „0.579999946057796478271484375” i jak będzie (float) 0.58działać? Powiedziałeś wcześniej, że nie ma konwersji, ponieważ informacje mogą zostać utracone, więc jak to się stanie, że obsada będzie działać?
SexyBeast

8
1. Użyłem kalkulatora Windows, który używa do 34 cyfr. 2. Powiedziałem, że nie ma niejawnej konwersji. Rzutowanie jest jawną konwersją, więc jawnie mówisz kompilatorowi, że chcesz, aby ta konwersja była dozwolona.
Jeffrey Sax,

Ciekawe ... Mam nadzieję, że nadal monitorujesz ten post. Wprowadzono mnie do przyrostków, które wcześniej napotkałem na „m” podczas oglądania transmisji internetowej. W jaki sposób kalkulator systemu Windows wyświetla wartość .58 jako wartość zmiennoprzecinkową? Wypróbowałem kilka przycisków, które wyglądały, jakby wymuszały to, ale nie ... ciągle otrzymywałem 0,58. Muszę szanować człowieka, który tak dobrze zna swoje narzędzia ...
user1585204

Rozumiem całą rzecz F dla float, ale dlaczego? Kiedy zadeklarowałeś zmienną, jest deklarowana jako zmiennoprzecinkowa, więc po kompilacji powinna wiedzieć, że ta wartość jest zmiennoprzecinkowa. A kiedy deklarujesz podwójne to samo. To nie ma sensu, ponieważ masz float myvalue = 2.4; teraz po kompilacji kompilator powinien już wiedzieć, że float jest typem zmiennej. Czy coś mi brakuje? Dzięki!
Frank G.

@FrankG. Z dwóch powodów: 1. Wyrażenie 2.4jest wszędzie interpretowane jako podwójne. 2. Niejawne konwersje zawężające (np. Z double na float) są niedozwolone. Jeśli chcesz zrobić wyjątek od tych zasad, musisz mieć bardzo dobry powód. Oszczędność jednego naciśnięcia klawisza raczej nie wystarczy.
Jeffrey Sax

36

Ponieważ istnieje kilka typów numerycznych, że kompilator może wykorzystać do reprezentowania wartości 0.58: float, doublei decimal. O ile nie zgadzasz się z tym, że kompilator wybiera jeden za Ciebie, musisz ujednoznacznić.

Dokumentacja dla doublestwierdza, że ​​jeśli sam nie określisz typu, kompilator zawsze wybierze doublejako typ dowolnego rzeczywistego literału numerycznego:

Domyślnie prawdziwy literał numeryczny po prawej stronie operatora przypisania jest traktowany jako double. Jeśli jednak chcesz, aby liczba całkowita była traktowana jako podwójna, użyj sufiksu d lub D.

Dołączenie sufiksu ftworzy float; przyrostek dtworzy double; przyrostek mtworzy decimal. Wszystkie te działają również z wielkich liter.

Jednak to wciąż nie wystarczy, aby wyjaśnić, dlaczego to się nie kompiluje:

float timeRemaining = 0.58;

Brakująca połowa odpowiedzi polega na tym, że konwersja z wersji double 0.58na float timeRemainingpotencjalnie traci informacje, więc kompilator odmawia jej niejawnego zastosowania. Jeśli dodasz jawne rzutowanie, zostanie wykonana konwersja; jeśli dodasz fsufiks, konwersja nie będzie potrzebna. W obu przypadkach kod byłby następnie kompilowany.


ale jak to może być podwójne lub dziesiętne, jeśli zmienna jest liczbą zmiennoprzecinkową, czegoś mi brakuje
Thomas

@BlazArt Tak, zdanie, które stwierdza, że ​​kompilator nawet nie próbuje sprawdzić celu przypisania. Powód tego zostanie pogrzebany w wyborach projektowych dokonanych przez zespół kompilatorów. Wydaje mi się, że najlepiej jest wyrazić się otwarcie w obliczu ewentualnego zamieszania, ponieważ implementacja była zbyt kosztowna, zamiast po prostu mieć dwie zasady, jedną za inti jedną za double.
Adam Houldsworth,

@BlazArt: Właśnie skończyłem opracowywać odpowiedź, spójrz ponownie.
Jon,

1
Jeśli chodzi o utratę informacji, czy możesz mi powiedzieć, jak faktycznie odbywa się przesyłanie w odniesieniu do formatu IEEE 754, w którym są przechowywane? Podwójna ma wyższą precyzję, więc czy to oznacza, że ​​rzut z pływaka na podwójny zawsze będzie działał, tak jak double a = 0.69f;?
SexyBeast

@Cupidvogel: Tak, specyfikacja gwarantuje (w §6.1.2), że „inne niejawne konwersje liczbowe nigdy nie tracą żadnych informacji”, co obejmuje między innymi konwersję floatna double.
Jon

2

Problem polega na tym, że .NET, aby umożliwić wykonywanie niektórych typów niejawnych operacji obejmujących floati double, musiał albo jawnie określić, co powinno się zdarzyć we wszystkich scenariuszach obejmujących operandy mieszane, albo zezwolić na niejawne konwersje między typami, które mają być wykonywane w jednym tylko kierunek; Microsoft zdecydował się pójść za przykładem Javy, dopuszczając kierunek, który czasami sprzyja precyzji, ale często rezygnuje z poprawności i generalnie powoduje kłopoty.

W prawie wszystkich przypadkach, biorąc plik double wartość, która jest najbliższa określonej wielkości liczbowej i przypisując ją do a float, daje floatwartość, która jest najbliższa tej samej wielkości. Istnieje kilka przypadków narożnych, takich jak wartość 9,007,199,791,611,905; najlepsza floatreprezentacja wyniosłaby 9 007 200 328 482 816 (co jest mniej o 536 870 911), ale rzucenie najlepszej doublereprezentacji (tj. 9 007 199 791 611 904) floatdaje 9 007 199 254 740 992 (co jest mniejsze o 536 870 913). Jednak ogólnie rzecz biorąc, przekształcenie najlepszej doublereprezentacji pewnej wielkości na floatda albo najlepszą możliwą floatreprezentację, albo jedną z dwóch reprezentacji, które są zasadniczo równie dobre.

Zauważ, że to pożądane zachowanie ma zastosowanie nawet w skrajnych przypadkach; na przykład najlepsza floatreprezentacja wielkości 10 ^ 308 jest zgodna z floatreprezentacją uzyskaną przez przekształcenie najlepszej doublereprezentacji tej wielkości. Podobnie najlepsza floatreprezentacja 10 ^ 309 odpowiada floatreprezentacji uzyskanej przez przekształcenie najlepszej doublereprezentacji tej wielkości.

Niestety, konwersje w kierunku, który nie wymaga wyraźnego rzutowania, rzadko są tak dokładne. Konwertowanie najlepszychfloat reprezentacji wartości na doublerzadko daje coś szczególnie bliskiego najlepszej doublereprezentacji tej wartości, aw niektórych przypadkach wynik może różnić się o setki rzędów wielkości (np. Przekształcenie najlepszej floatreprezentacji 10 ^ 40 na doubleda wartość, która w porównaniu jest większa niż najlepsza doublereprezentacja 10 ^ 300.

Niestety, reguły konwersji są tym, czym są, więc trzeba żyć z używaniem głupich typów i przyrostków podczas konwersji wartości w „bezpiecznym” kierunku i uważać na niejawne typy w niebezpiecznym kierunku, które często dają fałszywe wyniki.

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.