Próbuję dostać sumę 1 + 2 + ... + 1000000000
, ale jestem coraz śmieszne wyników w PHP i node.js .
PHP
$sum = 0;
for($i = 0; $i <= 1000000000 ; $i++) {
$sum += $i;
}
printf("%s", number_format($sum, 0, "", "")); // 500000000067108992
Node.js
var sum = 0;
for (i = 0; i <= 1000000000; i++) {
sum += i ;
}
console.log(sum); // 500000000067109000
Prawidłową odpowiedź można obliczyć za pomocą
1 + 2 + ... + n = n(n+1)/2
Prawidłowa odpowiedź = 500000000500000000 , więc postanowiłem spróbować innego języka.
UDAĆ SIĘ
var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
sum += i
}
fmt.Println(sum) // 500000000500000000
Ale działa dobrze! Więc co jest nie tak z moim kodem PHP i Node.js?
Być może jest to problem interpretowanych języków i dlatego działa w skompilowanym języku takim jak Go? Jeśli tak, to czy inne interpretowane języki, takie jak Python i Perl, miałyby ten sam problem?
Odpowiedzi:
Python działa:
Lub:
int
Auto Pythona awansuje do Pythona,long
który obsługuje dowolną precyzję. Wywoła poprawną odpowiedź na platformach 32- lub 64-bitowych.Można to zaobserwować, podnosząc 2 do mocy znacznie większej niż szerokość bitowa platformy:
Możesz zademonstrować (za pomocą Pythona), że błędne wartości otrzymywane w PHP są spowodowane tym, że PHP promuje się jako zmiennoprzecinkowe, gdy wartości są większe niż 2 ** 32-1:
źródło
Twój kod Go używa arytmetyki liczb całkowitych z wystarczającą liczbą bitów, aby dać dokładną odpowiedź. Nigdy nie dotknąłem PHP ani Node.js, ale z wyników podejrzewam, że matematyka jest wykonywana przy użyciu liczb zmiennoprzecinkowych i dlatego należy oczekiwać, że nie będzie dokładna dla liczb tej wielkości.
źródło
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which results in a number beyond the bounds of the integer type will return a float instead.
- php.net/manual/en/language.types.integer.phpPowodem jest to, że wartość zmiennej całkowitej
sum
przekracza wartość maksymalną. Asum
masz jest wynikiem pływak-punktowej arytmetyki która polega zaokrągleń. Ponieważ inne odpowiedzi nie wspominały o dokładnych limitach, postanowiłem to opublikować.Maksymalna wartość całkowita dla PHP dla:
Oznacza to, że używasz 32-bitowego procesora, 32-bitowego systemu operacyjnego lub 32-bitowej wersji PHP. Można go znaleźć za pomocą
PHP_INT_MAX
. Zostaniesum
obliczony poprawnie, jeśli zrobisz to na komputerze 64-bitowym.Maksymalna wartość całkowita w JavaScript to 9007199254740992 . Największa dokładna wartość całki, z którą możesz pracować, to 2 53 (wzięte z tego pytania ).
sum
Przekroczy ten limit.Jeśli wartość całkowita nie przekracza tych limitów, oznacza to, że jesteś dobry. W przeciwnym razie będziesz musiał szukać bibliotek liczb całkowitych o dowolnej precyzji.
źródło
Oto odpowiedź w C, dla kompletności:
Kluczem w tym przypadku jest użycie typu danych C99
long long
. Zapewnia największą prymitywną pamięć C, którą może zarządzać, i działa naprawdę, bardzo szybko. Tenlong long
typ będzie również działał na większości dowolnych komputerów 32- lub 64-bitowych.Jest jedno zastrzeżenie: kompilatory dostarczone przez Microsoft wyraźnie nie obsługują 14-letniego standardu C99, więc uruchomienie go w Visual Studio to crapshot.
źródło
long long
w standardzie C ++ 11. Jednak od kilku lat jest to rozszerzenie MSVC ++ i g ++.movabsq $500000000500000000, %rsi
gcc -O3
lubclang -O3
. Nie znam nazwy konkretnej optymalizacji. Zasadniczo kompilator zauważa, że wynik pętli nie zależy od żadnego argumentu i oblicza go w czasie kompilacji.Domyślam się, że gdy suma przekracza pojemność natywnego
int
(2 31 -1 = 2 147 483 647), Node.js i PHP przełączają się na reprezentację zmiennoprzecinkową i zaczynają pojawiać się błędy zaokrągleń. Język taki jak Go prawdopodobnie będzie starał się trzymać z liczbą całkowitą (np. 64-bitowe liczby całkowite) tak długo, jak to możliwe (jeśli rzeczywiście nie zaczął się od tego). Ponieważ odpowiedź mieści się w 64-bitowej liczbie całkowitej, obliczenia są dokładne.źródło
Skrypt Perla daje nam oczekiwany wynik:
źródło
4.99999999067109e+017
na Perlu v5.16.1 MSWin32-x86.bignum
lubbigint
. Oba są modułami podstawowymi, to znaczy są instalowane z Perlem v5.8.0 lub nowszym. Zobaczhttp://perldoc.perl.org/bignum.html
ihttp://perldoc.perl.org/bigint.html
Odpowiedź na to jest „zaskakująco” prosta:
Po pierwsze - jak większość z was może wiedzieć - 32-bitowa liczba całkowita wynosi od -2 147 483 648 do 2147 483 647 . Co się stanie, jeśli PHP otrzyma wynik, który jest WIĘKSZY niż ten?
Zwykle można się spodziewać natychmiastowego „przepełnienia”, powodując, że 2 147 483 647 + 1 zmienia się w -2 147 147 483 648 . Tak jednak nie jest. JEŻELI PHP napotyka większą liczbę, zwraca wartość FLOAT zamiast INT.
http://php.net/manual/en/language.types.integer.php
To powiedziawszy, i wiedząc, że implementacja PHP FLOAT jest zgodna z formatem podwójnej precyzji IEEE 754, oznacza, że PHP jest w stanie poradzić sobie z liczbami do 52 bitów, bez utraty precyzji. (W systemie 32-bitowym)
Tak więc w punkcie, w którym twoja suma osiągnie 9 007 199 254 440,992 (czyli 2 ^ 53 ) Wartość zmiennoprzecinkowa zwrócona przez matematykę PHP nie będzie już wystarczająco precyzyjna.
Ten przykład pokazuje punkt, w którym PHP traci precyzję. Po pierwsze, ostatni znaczący bit zostanie usunięty, co spowoduje, że pierwsze 2 wyrażenia będą miały taką samą liczbę - co nie jest.
Od TERAZ cała matematyka pójdzie nie tak podczas pracy z domyślnymi typami danych.
Nie wydaje mi się Myślę, że jest to problem języków, które nie mają bezpieczeństwa typu. Podczas gdy przepełnienie liczb całkowitych, jak wspomniano powyżej, BĘDZIE występować w każdym języku, który używa stałych typów danych, języki bez bezpieczeństwa typu mogą próbować wychwycić to z innymi typami danych. Jednak po osiągnięciu „naturalnej” (podanej przez system) granicy - mogą zwrócić wszystko, ale właściwy wynik.
Jednak każdy język może mieć różne wątki dla takiego scenariusza.
źródło
Inne odpowiedzi już wyjaśniły, co się tutaj dzieje (jak zwykle zmiennoprzecinkowa).
Jednym z rozwiązań jest użycie wystarczająco dużej liczby całkowitej lub mieć nadzieję, że język wybierze ją w razie potrzeby.
Innym rozwiązaniem jest użycie algorytmu sumowania, który zna problem precyzji i działa na jego podstawie. Poniżej znajduje się to samo podsumowanie, najpierw z 64-bitową liczbą całkowitą, następnie z 64-bitową zmiennoprzecinkową, a następnie ponownie używając zmiennoprzecinkowego, ale z algorytmem sumowania Kahana .
Napisane w C #, ale to samo dotyczy innych języków.
Podsumowanie Kahana daje piękny wynik. Obliczenie zajmuje oczywiście więcej czasu. To, czy chcesz go użyć, zależy od a) wydajności i potrzeb precyzji oraz b) tego, jak Twój język obsługuje typy danych liczb całkowitych i zmiennoprzecinkowych.
źródło
Jeśli masz 32-bitowy PHP, możesz go obliczyć za pomocą bc :
W Javascript musisz użyć dowolnej biblioteki liczb, na przykład BigInteger :
Nawet w przypadku języków takich jak Go i Java w końcu będziesz musiał użyć dowolnej biblioteki liczb, twoja liczba była po prostu wystarczająco mała dla 64-bitowych, ale zbyt wysoka dla 32-bitowych.
źródło
W Ruby:
Drukuje
500000000500000000
, ale zajmuje 4 minuty na moim Intel i7 2,6 GHz.Magnuss i Jaunty mają znacznie więcej Rubiego:
Aby uruchomić test porównawczy:
źródło
Używam node-bigint do dużych liczb całkowitych:
https://github.com/substack/node-bigint
Nie jest tak szybki, jak coś, co może używać natywnych 64-bitowych danych do tego dokładnego testu, ale jeśli dojdziesz do większej liczby niż 64-bit, używa libgmp pod maską, która jest jedną z najszybszych bibliotek o dowolnej precyzji.
źródło
wieki w rubinie, ale daje poprawną odpowiedź:
źródło
Aby uzyskać poprawny wynik w php, myślę, że będziesz musiał użyć operatorów matematycznych BC: http://php.net/manual/en/ref.bc.php
Oto poprawna odpowiedź w Scali. Musisz użyć Longs, w przeciwnym razie przepełnisz liczbę:
źródło
Ten problem jest naprawdę fajny.
Załóżmy, że zamiast tego było to 1-100.
1 + 2 + 3 + 4 + ... + 50 +
100 + 99 + 98 + 97 + ... + 51
= (101 + 101 + 101 + 101 + ... + 101) = 101 * 50
Formuła:
Dla N = 100: Wyjście = N / 2 * (N + 1)
Dla N = 1e9: Wyjście = N / 2 * (N + 1)
Jest to o wiele szybsze niż przeglądanie wszystkich tych danych. Twój procesor ci za to podziękuje. A oto ciekawa historia dotycząca tego właśnie problemu:
http://www.jimloy.com/algebra/gauss.htm
źródło
Daje to poprawny wynik w PHP poprzez wymuszenie rzutowania liczbami całkowitymi.
źródło
Common Lisp jest jednym z najszybciej interpretowanych języków * i domyślnie poprawnie obsługuje dowolne duże liczby całkowite. W SBCL zajmuje to około 3 sekund :
źródło
Nie mam wystarczającej reputacji, aby skomentować odpowiedź Common Lisp @ postfuturist, ale można ją zoptymalizować w ~ 500ms za pomocą SBCL 1.1.8 na moim komputerze:
źródło
Racket v 5.3.4 (MBP; czas w ms):
źródło
Działa dobrze w Rebol:
To było przy użyciu Rebol 3, który pomimo 32-bitowej kompilacji używa 64-bitowych liczb całkowitych (w przeciwieństwie do Rebol 2, który używał 32-bitowych liczb całkowitych)
źródło
Chciałem zobaczyć, co się wydarzyło w skrypcie CF.
Mam 5,00000000067E + 017
To był całkiem fajny eksperyment. Jestem całkiem pewien, że przy większym wysiłku mógłbym to kodować nieco lepiej.
źródło
ActivePerl v5.10.1 w 32-bitowych oknach, Intel Core2duo 2.6:
wynik: 5,00000000067109e + 017 w 5 minut.
Skrypt „use bigint” działał przez dwie godziny i działał dłużej, ale przestałem. Za wolno.
źródło
Dla kompletności w Clojure (piękny, ale niezbyt wydajny):
źródło
AWK:
daje taki sam zły wynik jak PHP:
Wygląda na to, że AWK używa liczb zmiennoprzecinkowych, gdy liczby są naprawdę duże, więc przynajmniej odpowiedź brzmi: właściwy rząd wielkości.
Przebiegi testowe:
źródło
Kategoria inny tłumaczony język:
Tcl:
Jeśli używasz Tcl 8.4 lub starszego, zależy to, czy został skompilowany z 32- lub 64-bitowym kodem. (8.4 to koniec życia).
Jeśli używasz Tcl 8.5 lub nowszego, który ma dowolne duże liczby całkowite, wyświetli poprawny wynik.
Umieszczam test w proc, aby uzyskać kompilację bajtów.
źródło
W przypadku kodu PHP odpowiedź jest tutaj :
źródło
Port:
Wyniki w
500000000500000000
. (zarówno w systemie Windows / mingw / x86, jak i osx / clang / x64)źródło
Erlang działa:
źródło
Zabawne, PHP 5.5.1 daje 499999999500000000 (w ~ 30s), podczas gdy Dart2Js daje 500000000067109000 (czego należy się spodziewać, ponieważ wykonywany jest JS). CLI Dart daje właściwą odpowiedź ... natychmiast.
źródło
Erlang daje również oczekiwany wynik.
sum.erl:
I używając go:
źródło
Pogawędka:
źródło