Po co pisać 1000000000 jako 1000 * 1000 * 1000 w C?


142

W kodzie stworzonym przez Apple jest taka linia:

CMTimeMakeWithSeconds( newDurationSeconds, 1000*1000*1000 )

Czy jest jakiś powód, aby wyrazić 1,000,000,000jako 1000*1000*1000?

Dlaczego nie 1000^3o to chodzi?


45
Przejrzystość i czytelność kodu. Nie możesz wstawić lub 'jako separatorów w C, więc następną najlepszą rzeczą jest obliczenie wartości z mnożenia. ^ jest niepowiązanym operatorem w C - wyłączne OR.
Nie będę istnieć Idonotistnieje

54
jest szczególnie używany przez okresy takie jak: 2 * 60 * 60 .. łatwo zauważyć, że trwa 2 godziny ..
Idali

53
Po pierwsze, podejrzewam, że 1000000000 to nieprawidłowa składnia
paparazzo

15
Nawiasem mówiąc, współczesne języki programowania mają alternatywny sposób pisania tego samego, np. W Swift 1_000_000_000. Jednak przy stałych czasowych jest to trudniejsze. Łatwiej jest pisać 30 * 60(30 minut w sekundach) niż pisać 1800. W rzeczywistości istnieją języki, które pozwolą ci pisać jednostki, np. metersPozwalając ci chronić się przed złymi zadaniami.
Sulthan

29
^jest XOR, a nie wykładnikiem ani operatorem potęgi.
Zapisz

Odpowiedzi:


200

Jednym z powodów deklarowania stałych w sposób multiplikatywny jest poprawa czytelności bez wpływu na wydajność w czasie wykonywania. Również po to, by wskazać, że pisarz myślał o liczbie w sposób multiplikatywny.

Rozważ to:

double memoryBytes = 1024 * 1024 * 1024;

Jest wyraźnie lepszy niż:

double memoryBytes = 1073741824;

ponieważ ta ostatnia nie wygląda na pierwszy rzut oka na trzecią potęgę 1024.

Jak wspomniał Amin Negm-Awad, ^operatorem jest binarny XOR. W wielu językach brakuje wbudowanego operatora potęgowania w czasie kompilacji, stąd mnożenie.


13
A w językach, które mają operator potęgowania, niekoniecznie jest to „^”. Na przykład w języku Fortran jest to „**”.
jamesqf

4
Powinieneś także dołączyć link wskazujący na ważne zastrzeżenie, podane w odpowiedzi poniżej, z @chux: stackoverflow.com/a/40637622/1841533 (szczególnie, gdy OP oznaczono "c", co jest bardzo podatne na operacja poboczna wydaje się mieć wszystkie terminy ograniczone do mniejszego typu, a zatem problem mnożenia może spowodować przepełnienie). securecoding.cert.org/confluence/display/c/… może pomóc uniknąć tych w ogólnym przypadku?
Olivier Dulac

4
Powinniśmy również zauważyć, że obliczenia są wykonywane w czasie kompilacji. Standard C wymaga, aby implementacja była w stanie obliczyć wyrażenia stałe w czasie kompilacji dla różnych funkcji języka i możemy bezpiecznie założyć, że jest to prawda, gdy używane jest wyrażenie stałe, jak w tym przykładzie.
ouah

13
Przechowujesz podwójną ilość pamięci? Wydaje się, że jest to potencjalne źródło błędu.
JAB

7
@supercat Jestem tego świadomy, ale używając double możesz mieć przypadek, w którym, powiedzmy, potrzebujesz części zakresu pamięci, dzielisz przez, xaby uzyskać rozmiar podzakresu ... i nagle masz ułamek bajt, który może wymagać dodatkowej logiki do skompensowania.
JAB

73

Dlaczego nie 1000^3?

Wynik 1000^3to 1003. ^jest operatorem bit-XOR.

Nawet to nie dotyczy samego Q, dodaję wyjaśnienie. x^ynie nie zawsze ocenia się x+ytak jak w przykładzie pytający użytkownika. Musisz wszystko xorować. W przypadku przykładu:

1111101000 (1000₁₀)
0000000011 (3₁₀)
1111101011 (1003₁₀)

Ale

1111101001 (1001₁₀)
0000000011 (3₁₀)
1111101010 (1002₁₀)

3
proszę pana, nie rozumiem, jak 1003 ^ 3 to 1003. Kalkulator Google i Mac pokazuje 1000 ^ 3 = 1 000 000 000. możesz wytłumaczyć?
Mahesh

57
^Środki napędu XOR C / C ++ / cel-C itd. W kalkulatorów zwykle środki zasilania x do-Y.
Tamás Zahola

5
Aha, bity 1000 i 3 nie nakładają się. To wygląda tak źle.
Yakk - Adam Nevraumont

19
Bity nakładają się. Ale nie 1. : -]
Amin Negm-Awad

6
@Yakk: rzeczywiście, wygląda to tak źle! ... Mam nadzieję, że wielu ludzi nie pomyśli, że "A ^ B" zawsze daje A + B (ale obawiam się, że może to być ...)
Olivier Dulac

71

Istnieją powody, aby nie używać 1000 * 1000 * 1000.

Z 16-bitowymi int, 1000 * 1000przelewa. Dlatego używanie 1000 * 1000 * 1000ogranicza przenośność.

W przypadku wersji 32-bitowej intnastępuje przepełnienie pierwszego wiersza kodu.

long long Duration = 1000 * 1000 * 1000 * 1000;  // overflow
long long Duration = 1000000000000;  // no overflow, hard to read

Zaproponuj, aby wartość wiodąca odpowiadała typowi miejsca docelowego pod względem czytelności, przenośności i poprawności.

double Duration = 1000.0 * 1000 * 1000;
long long Duration = 1000LL * 1000 * 1000 * 1000;

Można również użyć prostej enotacji dla wartości, które są dokładnie reprezentowane jako double. Oczywiście prowadzi to do wiedzy, czy doublemoże dokładnie reprezentować wartość liczby całkowitej - coś niepokojącego w przypadku wartości większych niż 1e9. (Zobacz DBL_EPSILONi DBL_DIG).

long Duration = 1000000000;
// vs.
long Duration = 1e9;

5
Bardzo ważna uwaga! securecoding.cert.org/confluence/display/c/… może pomóc w wielu przypadkach?
Olivier Dulac

2
A doublemoże dokładnie reprezentować wszystkie liczby całkowite do 2 ^ 53 ≈ 9e15.
Edgar Bonet

3
@EdgarBonet Prawda, że binary64 może reprezentować liczbę całkowitą do około 9e15. Ale C nie określa doubleużycia binary64, mimo że jest on bardzo powszechnie używany. Zgodnie ze specyfikacją C, wartości do 1e9 lub więcej są dokładnie reprezentowalne. Zależy to od tego, czy chcesz kodować zgodnie ze specyfikacją, czy polegać na powszechnej praktyce.
chux - Przywróć Monikę

4
@Patrick Oba 1000i 1000000000000stałymi liczbami całkowitymi . Każdy niezależnie ma rodzaj wybrany z int, longlub long long. Kompilator używa pierwszego typu z tych 3, w których mieści się stała całkowita . 1000 * 1000 * 1000 * 1000odbywa się z intmatematyką, jak każdy 1000w pliku int. Produkt przepełnia 32-bitowy int. 1000000000000 z pewnością można przedstawić jako long long(lub węższy) - brak przepełnienia. Typ celu long long Durationnie wpływa na tę „prawą stronę oznaczenia =”.
chux - Przywróć Monikę

2
Ważne jest umieszczenie szerszego typu jako pierwszego w mnożeniu. Z 16-bitowymi int, long x = 1000 * 1000 * 1000L;by przelewać, natomiast long x = 1000L * 1000 * 1000;nie.
ex nihilo

51

Dla czytelności.

Umieszczenie przecinków i spacji między zerami ( 1 000 000 000lub 1,000,000,000) spowodowałoby błąd składniowy, a umieszczenie 1000000000w kodzie utrudnia dokładne sprawdzenie, ile jest zer.

1000*1000*1000pokazuje, że jest to 10 ^ 9, ponieważ nasze oczy mogą łatwiej przetwarzać fragmenty. Ponadto nie ma żadnych kosztów wykonania, ponieważ kompilator zastąpi go stałą 1000000000.


5
FYI, istnieje koncepcja separatorów cyfr, o której ostatnio się dowiedziałem. Java ma to już od jakiegoś czasu i C # 7.0 może to dostać. Chciałbym, żeby wszystkie języki miały tę przyjemną dla oka funkcję. :)
iheartcsharp

1
W zależności od kontekstu użycie 1,000,000,000nie spowodowałoby błędu składniowego, oznaczałoby po prostu coś innego. Na przykładCMTimeMakeWithSeconds( newDurationSeconds, 1,000,000,000 )
Ross Ridge

2
@ JMS10 C # już go ma, jeśli zainstalujesz wersję podglądu VS15, można zapisać jako1_000_000_000
user1306322

2
Python również staje _się separatorem :)
Wayne Werner

2
A C ++ niedawno otrzymał 'separator w C ++ 14, więc możesz użyć 1'000'000'000. (Został wybrany, ponieważ 1,000,000,000mógł zostać błędnie zinterpretowany jako operator przecinka lub 4 różne parametry i _1_000_000_000jest prawidłową (ale prawdopodobnie złą) nazwą zmiennej.)
Justin Time - Przywróć Monikę

27

Dla czytelności. Dla porównania, Java obsługuje _liczby w celu poprawy czytelności (po raz pierwszy zaproponowane przez Stephena Colebourne jako odpowiedź na PROPOZYCJĘ Dereka Fostera: Binary Literals for Project Coin / JSR 334). 1_000_000_000Tutaj można by pisać .

W przybliżeniu w porządku chronologicznym, od najstarszego wsparcia do najnowszego:

Jest to stosunkowo nowa funkcja dla języków, aby zdać sobie sprawę, że powinny obsługiwać (a także jest Perl). Jak w doskonałej odpowiedzi chux @, 1000*1000...jest to częściowe rozwiązanie, ale otwiera programistę na błędy wynikające z przepełnienia mnożenia, nawet jeśli wynik końcowy jest duży.


Wiele współczesnych języków programowania ma to samo, np. Swift. Nic nowego.
Sulthan

AFAIK, to pochodzi od Perla. PL / M użył $ do tego samego celu, np .: 0100 $ 0010B
ninjalj

1
Jest to jednak dość nowe. Funkcja Java ma prawdopodobnie 5 lat. Większość innych języków obsługujących tę składnię jest całkiem nowa - sam Swift ma zaledwie kilka lat. Python dodaje obsługę w 3.6, która nie została jeszcze wydana.
johncip

2
Ada obsługuje podkreślenia w literałach całkowitych od 33 lat.
Peter - Przywróć Monikę

1
@djechlin: Pozwoliłem sobie dodać więcej informacji, mniej więcej w porządku chronologicznym. Myliłem się wcześniej, sądząc po wątku Project Coin, Stephen Colebourne prawdopodobnie wziął pomysł podkreślenia w literałach całkowitych z Fandomu i / lub Rubiego. Ruby prawdopodobnie wziął ten pomysł od Perla, a Perl od Ady.
ninjalj

7

Może być łatwiejsze do przeczytania i skojarzenia z 1,000,000,000formularzem.

Z technicznego punktu widzenia wydaje mi się, że nie ma różnicy między bezpośrednią liczbą a mnożeniem. Kompilator i tak wygeneruje ją jako stałą liczbę miliardów.

Jeśli mówisz o celu-c, to 1000^3nie zadziała, ponieważ nie ma takiej składni dla pow (jest to xor). Zamiast tego pow()można użyć funkcji. Ale w takim przypadku nie będzie optymalna, będzie to wywołanie funkcji wykonawczej, a nie stała wygenerowana przez kompilator.


3

Aby zilustrować powody, rozważ następujący program testowy:

$ cat comma-expr.c && gcc -o comma-expr comma-expr.c && ./comma-expr
#include <stdio.h>

#define BILLION1 (1,000,000,000)
#define BILLION2 (1000^3)

int main()
{
        printf("%d, %d\n", BILLION1, BILLION2);
}
0, 1003
$

1
@pjvandehaar Nie polecałbym nauki języka poprzez czytanie artykułów Wikipedii.
Peter - Przywróć Monikę

0

Innym sposobem osiągnięcia podobnego efektu w C dla liczb dziesiętnych jest użycie dosłownej notacji zmiennoprzecinkowej - o ile double może reprezentować żądaną liczbę bez utraty precyzji.

IEEE 754 64-bitowy double może bez problemu reprezentować dowolną nieujemną liczbę całkowitą <= 2 ^ 53. Zwykle długie podwójne (80 lub 128 bitów) może sięgać nawet dalej. Konwersje zostaną wykonane w czasie kompilacji, więc nie ma narzutu czasu wykonywania i prawdopodobnie otrzymasz ostrzeżenia, jeśli nastąpi nieoczekiwana utrata precyzji i masz dobry kompilator.

long lots_of_secs = 1e9;
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.