Zero jako stała?


15

Ostatnio natknąłem się na ten idiom programowania:

const float Zero = 0.0;

który jest następnie wykorzystywany w porównaniach:

if (x > Zero) {..}

Czy ktoś może wyjaśnić, czy jest to naprawdę bardziej wydajne, czytelne lub możliwe do utrzymania niż:

if (x > 0.0) {..}

UWAGA: Mogę wymyślić inne powody, by zdefiniować tę stałą. Zastanawiam się tylko nad jej użyciem w tym kontekście.


31
Programiści planują przenieść kod do wszechświata, w którym prawa matematyki są różne?
vaughandroid

6
Poważnie, nie mogę wymyślić choćby jednego dobrego powodu. Jedyne wyjaśnienia, które mogę wymyślić, to nadgorliwe standardy kodowania lub niektórzy deweloperzy, którzy słyszeli „magiczne liczby są złe”, ale nie rozumieją, dlaczego (lub co mogłoby stanowić magiczną liczbę) ...
vaughandroid

@Baqueta-alternatywny wszechświat? Myślę, że już tam mieszkali! Jeśli chodzi o liczby magiczne, zgadzam się, jednak stosuję ogólną zasadę, że wszystko oprócz 0 i 1 powinno być stałe.
NWS

1
Jeśli xma typ float, x > 0.0wymusza awans do double, który może być mniej wydajny. To nie jest dobry powód, aby korzystać nazwany stałą jednak tylko dla upewniając się swoimi stałymi mieć odpowiedni typ (np 0f, float(0)lub decltype(x)(0)).
Mike Seymour

1
@JoSo: aby być uczciwym, typ 13.37nie jest float, to jest double. Więc jeśli chciałeś, floatto możliwe, że twój nauczyciel miał rację. W niektórych kontekstach (np. Przypisanie do liczby zmiennoprzecinkowej) 13.37zostanie domyślnie przekonwertowane na floatżądany, aw innych kontekstach (np. Dedukcja typu szablonu) nie będzie, podczas gdy static const floatzawsze zaczyna się zgodnie z zamierzonym typem. Dlatego bardziej bezpieczny dla typu. Pamiętaj, że tak też będzie 13.37f! Istnieją jednak inne powody do unikania makra niż „bezpieczeństwo typu”, więc równie prawdopodobne jest, że korepetytor przedstawił ci kiepską argumentację.
Steve Jessop,

Odpowiedzi:


29

Możliwe przyczyny to buforowanie, nazywanie lub wymuszanie

Buforowanie (nie dotyczy)

Chcesz uniknąć kosztów utworzenia obiektu podczas aktu porównania. W Javie przykładem może być

BigDecimal zero = new BigDecimal ("0.0");

wiąże się to z dość ciężkim procesem tworzenia i jest lepiej obsługiwany przy użyciu dostarczonej metody statycznej:

BigDecimal zero = BigDecimal.ZERO;

Pozwala to na porównania bez ponoszenia powtarzających się kosztów tworzenia, ponieważ BigDecimal jest wstępnie buforowany przez JVM podczas inicjalizacji.

W przypadku tego, co opisałeś, prymityw wykonuje tę samą pracę. Jest to w dużej mierze zbędne pod względem buforowania i wydajności.

Nazewnictwo (mało prawdopodobne)

Pierwotny programista próbuje zapewnić jednolitą konwencję nazewnictwa dla wspólnych wartości w całym systemie. Ma to pewne zalety, szczególnie w przypadku rzadkich wartości, ale coś tak podstawowego, jak zero, jest tego warte tylko w przypadku wcześniejszej pamięci podręcznej.

Rodzaj wymuszenia (najprawdopodobniej)

Pierwotny programista próbuje wymusić określony typ pierwotny, aby upewnić się, że porównania są rzutowane na ich poprawny typ i ewentualnie na określoną skalę (liczbę miejsc dziesiętnych). Jest to w porządku, ale prosta nazwa „zero” jest prawdopodobnie niewystarczająco szczegółowa dla tego przypadku użycia, a ZERO_1DP jest bardziej odpowiednim wyrażeniem zamiaru.


2
+1 za typ wymuszania. Dodam, że w językach takich jak C ++, które pozwalają na przeciążanie operatora, zdefiniowanie stałej i użycie typedefspowoduje, że typ zmiennej będzie dokładnie w jednym miejscu i umożliwi jej zmianę bez konieczności zmiany kodu.
Blrfl

3
Typ wymuszania najprawdopodobniej nie jest tym, czego próbowali, ale jest to najlepsze wytłumaczenie, dlaczego można to zrobić!
NWS

7
Do wymuszania typu prawdopodobnie wolałbym po prostu użyć 0.0f.
Svish,

Typ wymuszania może czasem być użyteczny w vb.net, gdzie wykonywanie bitowych operatorów na bajtach daje wynik w bajtach. Mówienie byteVar1 = byteVar2 Or CB128wydaje się trochę ładniejsze niż byteVar1 = byteVar2 Or CByte(128). Oczywiście, posiadanie odpowiedniego przyrostka liczbowego dla bajtów byłoby jeszcze lepsze. Ponieważ C # promuje operandy bitowe do operatorów, intnawet jeśli wynik byłby w stanie zmieścić się w byte, problem nie jest tak istotny.
supercat

Nie jestem pewien, czy mam stałą nazwę jako zero dla „0”, ale czasami pomaga to w czytelności kodu; na przykład posiadanie tej stałej zerowej - „ROOT_TYPE_ID = 0” pomoże napisać instrukcję taką jak if (id! = ROOT_TYPE_ID) {..}
Tech Junkie

6

To z powodu „dręczenia narzędzi”

Możliwym powodem, którego nie wymieniono tutaj, jest to, że wiele wysokiej jakości narzędzi oznacza użycie magicznych liczb . Często złym zwyczajem jest wrzucanie do algorytmu liczb magicznych bez ich wyraźnego widocznego do późniejszego wprowadzenia zmian, szczególnie jeśli są one duplikowane w wielu miejscach w kodzie.

Tak więc, chociaż narzędzia te mają rację w kwestii zgłaszania takich problemów, często generują fałszywe alarmy w sytuacjach, w których te wartości są nieszkodliwe i najprawdopodobniej są statyczne lub po prostu są wartościami inicjalizacyjnymi.

A kiedy tak się dzieje, czasami stajesz przed wyborem:

  • oznaczanie ich jako fałszywych alarmów, jeśli narzędzie na to pozwala (zwykle ze specjalnie sformatowanym komentarzem, co jest denerwujące dla osób NIE używających tego narzędzia)
  • lub wyodrębnienie tych wartości do stałych, bez względu na to, czy ma to znaczenie, czy nie.

O wydajności

To chyba zależy od języka, ale jest to dość powszechne w Javie i nie ma wpływu na wydajność, ponieważ wartości są wstawiane w czasie kompilacji, jeśli są rzeczywistymi stałymi static final. Nie miałoby to wpływu na C lub C ++, jeśli zostaną zadeklarowane jako stałe lub nawet jako makra preprocesorów.


5

Może to mieć sens, ponieważ wyraźnie określa, Zeroże jest typu float.

Przynajmniej w C i C ++ wartość 0.0jest typu double, podczas gdy jej odpowiednikiem floatjest 0.0f. Więc zakładanie, xże porównujesz, jest zawsze floatpowiedzeniem

x > 0.0

a jednocześnie promować, xaby doubledopasować do rodzaju, 0.0który może prowadzić do problemów (szczególnie z testami równości). Porównanie bez konwersji byłoby oczywiście

x > 0.0f

co robi to samo co

float Zero = 0.0; // double 0.0 converted to float  
x > Zero

Niemniej jednak myślę, że znacznie bardziej użyteczne byłoby włączenie ostrzeżeń o konwersjach w kompilatorze zamiast zmuszania użytkowników do pisania niezręcznego kodu.


1

Po pierwsze, zero definiuje się jako float, a nie int. Oczywiście nie ma to wpływu na porównanie, ale w innych przypadkach, gdy używana jest ta stała, może to mieć znaczenie.

Nie widzę innego powodu, dla którego zostałby Zerotutaj ogłoszony stałym. To tylko styl kodowania i lepiej jest podążać za tym stylem, jeśli jest używany wszędzie indziej w tym programie.


1

Jest prawie na pewno dokładnie tak samo wydajny podczas wykonywania (chyba że twój kompilator jest bardzo prymitywny) i bardzo nieznacznie mniej wydajny podczas kompilacji.

Jeśli chodzi o to, czy jest to bardziej czytelne niż x > 0... pamiętaj, że są ludzie, którzy szczerze, szczerze sądzą, że COBOL był świetnym pomysłem i przyjemnością z nim współpracować - a potem są ludzie, którzy myślą dokładnie tak samo o C. (Plotka głosi, że że istnieją nawet programiści o takiej samej opinii na temat C ++!) Innymi słowy, nie osiągniesz ogólnej zgody w tej kwestii i prawdopodobnie nie warto o to walczyć.


0

[jest] jest to naprawdę bardziej wydajne, czytelne lub konserwowalne niż:

if (x > 0.0) {..}

Jeśli piszesz ogólny (tj. Nieswoisty dla kodu) kod, to bardzo możliwe. zero()Funkcja może mieć zastosowanie do wszelkich algebraicznych typu lub dowolnego typu, który jest dodatkiem grupa wrt. Może to być liczba całkowita, może być wartością zmiennoprzecinkową, może nawet być funkcją, jeśli twoja zmienna jest, powiedzmy, funkcją w jakiejś przestrzeni liniowej (np. X jest funkcją liniową postaci z -> a_x * z + b_x), a następnie zero()udostępnia funkcję, przy czym oba a i b są zero()typu podstawowego.

Można by się spodziewać takiego kodu w, powiedzmy, C ++, być może (chociaż zero()nie jest to bardzo powszechny AFAIK), lub w Julii i być może w innych językach.

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.