Jedną rzecz chciałbym zobaczyć byłoby uznanie, że double
na float
należy traktować jako rozszerzającej nawrócenia, podczas gdy float
do double
zwęża (*). Może się to wydawać sprzeczne z intuicją, ale zastanów się, co faktycznie oznaczają te typy:
- 0,1f oznacza „13 421 773,5 / 134 217 728, plus lub minus 1 / 268,435,456 lub więcej”.
- 0,1 naprawdę oznacza 3,602,879,701,896,397 / 36 028,797,018,963,968, plus lub minus 1/72 057 594,037,927,936 lub więcej ”
Jeśli ktoś ma double
najlepszą reprezentację wielkości „jedna dziesiąta” i konwertuje ją float
, wynikiem będzie „13 421 773,5 / 134 217 728, plus lub minus 1 / 268,435,456”, co jest poprawnym opisem wartości.
Dla kontrastu, jeśli ktoś ma float
najlepszą reprezentację wielkości „jedna dziesiąta” i konwertuje ją double
, wynik będzie wynosić „13 421 7733,5 / 134 217 728, plus lub minus 1/72 057 594,037,927,936 lub więcej” - poziom implikowanej dokładności co jest błędne ponad 53 milionami razy.
Chociaż standard IEEE-744 wymaga, aby matematyka zmiennoprzecinkowa była wykonywana tak, jakby każda liczba zmiennoprzecinkowa reprezentowała dokładną liczbę liczbową dokładnie w środku jej zakresu, nie należy zakładać, że wartości zmiennoprzecinkowe faktycznie reprezentują te dokładne wielkości liczbowe. Wymóg, aby przyjąć, że wartości znajdują się w środku ich zakresów, wynika z trzech faktów: (1) obliczenia muszą być wykonane tak, jakby argumenty miały pewne szczególne dokładne wartości; (2) spójne i udokumentowane założenia są bardziej pomocne niż niespójne lub nieudokumentowane; (3) jeśli ktoś zamierza przyjąć spójne założenie, żadne inne spójne założenie nie może być lepsze niż założenie, że ilość reprezentuje środek jego zakresu.
Nawiasem mówiąc, pamiętam jakieś 25 lat temu, ktoś wymyślił pakiet numeryczny dla C, który używał „typów zakresów”, z których każdy składał się z pary 128-bitowych liczb zmiennoprzecinkowych; wszystkie obliczenia zostałyby wykonane w taki sposób, aby obliczyć minimalną i maksymalną możliwą wartość dla każdego wyniku. Jeśli ktoś wykona duże, długie obliczenie iteracyjne i uzyska wartość [12.53401391134 12.53902812673], można mieć pewność, że choć wiele cyfr precyzji zostało utraconych z powodu błędów zaokrąglania, wynik nadal można rozsądnie wyrazić jako 12,54 (i to nie było t naprawdę 12,9 lub 53,2). Dziwię się, że nie widziałem żadnego wsparcia dla takich typów w żadnym z głównych języków, zwłaszcza że wydaje się, że dobrze pasują do jednostek matematycznych, które mogą działać na wielu wartościach równolegle.
(*) W praktyce często pomocne jest stosowanie wartości podwójnej precyzji do przechowywania obliczeń pośrednich podczas pracy z liczbami o pojedynczej precyzji, dlatego użycie wszystkich typów operacji może być denerwujące. Języki mogłyby pomóc, mając typ „rozmytego podwójnego”, który wykonywałby obliczenia jako podwójny i mógłby być swobodnie przesyłany do iz pojedynczego; byłoby to szczególnie pomocne, gdyby funkcje, które pobierają parametry typu double
i powrotu, double
mogły zostać oznaczone, aby automatycznie generowały przeciążenie, które akceptuje i zwraca zamiast tego „rozmyte podwójne”.