Я пытаюсь получить сумму 1 + 2 + ... + 1000000000
, но я получаю смешные результаты в PHP и 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
Правильный ответ можно рассчитать, используя
1 + 2 + ... + n = n(n+1)/2
Правильный ответ = 500000000500000000 , поэтому я решил попробовать другой язык.
ИДТИ
var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
sum += i
}
fmt.Println(sum) // 500000000500000000
Но это работает отлично! Так что же не так с моим кодом PHP и Node.js?
Возможно, это проблема интерпретируемых языков, и поэтому она работает на скомпилированном языке, таком как Go? Если да, будут ли другие интерпретируемые языки, такие как Python и Perl, иметь такую же проблему?
Ответы:
Python работает:
Или:
int
Авто Python продвигается к Python,long
который поддерживает произвольную точность. Он даст правильный ответ на 32 или 64-битных платформах.Это можно увидеть, подняв 2 до степени, намного превышающей ширину в битах платформы:
Вы можете продемонстрировать (с помощью Python), что ошибочные значения, которые вы получаете в PHP, объясняются тем, что PHP переводит в плавающее число, когда значения больше 2 ** 32-1:
источник
Ваш код Go использует целочисленную арифметику с достаточным количеством битов, чтобы дать точный ответ. Никогда не прикасался к PHP или Node.js, но, как я полагаю, по результатам математика выполняется с использованием чисел с плавающей запятой, и поэтому следует ожидать, что она не будет точной для чисел этой величины.
источник
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.phpПричина в том, что значение вашей целочисленной переменной
sum
превышает максимальное значение. Иsum
вы получите результат арифметики с плавающей точкой, которая включает в себя округление. Поскольку в других ответах не упоминались точные пределы, я решил опубликовать его.Максимальное целочисленное значение для PHP для:
Это означает, что вы используете 32-битный процессор или 32-битную ОС или 32-битную скомпилированную версию PHP. Это можно найти с помощью
PHP_INT_MAX
. Значениеsum
будет рассчитано правильно, если вы сделаете это на 64-битной машине.Максимальное целочисленное значение в JavaScript составляет 9007199254740992 . Наибольшее точное целое значение, с которым вы можете работать, составляет 2 53 (взято из этого вопроса ).
sum
Превышает этот предел.Если целочисленное значение не выходит за эти пределы, то вы в порядке. В противном случае вам придется искать целочисленные библиотеки произвольной точности.
источник
Вот ответ на C для полноты:
Ключ в этом случае использует тип данных C99
long long
. Он обеспечивает самое большое примитивное хранилище, с которым может работать C, и работает очень, очень быстро.long long
Тип будет также работать на большинстве 32 или 64-битной машине.Есть одно предостережение: компиляторы, предоставляемые Microsoft, явно не поддерживают 14-летний стандарт C99, поэтому запуск его в Visual Studio - непростая задача.
источник
long long
стандарт C ++ 11. Это было расширение MSVC ++ и g ++ в течение нескольких лет.movabsq $500000000500000000, %rsi
gcc -O3
илиclang -O3
. Я не знаю название конкретной оптимизации. По сути, компилятор замечает, что результат цикла не зависит от какого-либо аргумента, и вычисляет его во время компиляции.Я предполагаю, что когда сумма превышает емкость нативного
int
(2 31 -1 = 2 147 483 647), Node.js и PHP переключаются на представление с плавающей запятой, и вы начинаете получать ошибки округления. Такой язык, как Go, вероятно, будет пытаться придерживаться целочисленной формы (например, 64-разрядных целых) как можно дольше (если, действительно, это не началось с этого). Поскольку ответ помещается в 64-разрядное целое число, вычисление является точным.источник
Скрипт Perl дает нам ожидаемый результат:
источник
4.99999999067109e+017
на Perl v5.16.1 MSWin32-x86.bignum
илиbigint
. Оба являются основными модулями, то есть они устанавливаются с Perl v5.8.0 или выше. Смотритеhttp://perldoc.perl.org/bignum.html
иhttp://perldoc.perl.org/bigint.html
Ответ на этот вопрос «на удивление» прост:
Во-первых, как многие из вас знают, 32-разрядное целое число колеблется от -2 147 483 648 до 2 147 483 647 . Итак, что произойдет, если PHP получит результат, который БОЛЬШЕ, чем этот?
Обычно можно ожидать немедленного «переполнения», в результате которого 2 147 483 647 + 1 превратится в -2 147 483 648 . Тем не менее, это не так. Если PHP встречает большее число, он возвращает FLOAT вместо INT.
http://php.net/manual/en/language.types.integer.php
Это говорит о том, что знание того, что реализация PHP FLOAT соответствует формату двойной точности IEEE 754, означает, что PHP может работать с числами до 52 бит без потери точности. (В 32-битной системе)
Таким образом, в точке, где ваша сумма достигает 9 007 199 254 740 992 (что составляет 2 ^ 53 ), значение Float, возвращаемое PHP Maths, больше не будет достаточно точным.
В этом примере показана точка, в которой PHP теряет точность. Во-первых, последний значащий бит будет сброшен, в результате чего первые два выражения приведут к равному числу, а это не так.
С этого момента вся математика будет работать неправильно при работе с типами данных по умолчанию.
Я так не думаю. Я думаю, что это проблема языков, которые не имеют безопасности типов. Хотя целочисленное переполнение, как упомянуто выше, будет происходить на каждом языке, который использует фиксированные типы данных, языки без безопасности типов могут попытаться перехватить это с другими типами данных. Однако, как только они достигнут своей «естественной» (заданной системой) границы - они могут вернуть что угодно, но правильный результат.
Однако каждый язык может иметь разные потоки для такого сценария.
источник
Другие ответы уже объяснили, что здесь происходит (как обычно, с плавающей запятой).
Одно из решений состоит в том, чтобы использовать достаточно большой целочисленный тип или надеяться, что язык выберет один, если это необходимо.
Другое решение состоит в том, чтобы использовать алгоритм суммирования, который знает о проблеме точности и работает вокруг нее. Ниже вы найдете то же самое суммирование, сначала с 64-битным целым числом, затем с 64-битным с плавающей запятой, а затем снова с плавающей запятой, но с алгоритмом суммирования Кахана .
Написан на C #, но то же самое верно и для других языков.
Суммирование Кахана дает прекрасный результат. Конечно, вычисление занимает гораздо больше времени. От того, хотите ли вы его использовать, зависит а) от ваших требований к производительности и точности, и б) от того, как ваш язык обрабатывает целочисленные и типы данных с плавающей запятой.
источник
Если у вас есть 32-битный PHP, вы можете рассчитать его с помощью bc :
В Javascript вы должны использовать библиотеку произвольных чисел, например BigInteger :
Даже с такими языками, как Go и Java, вам в конечном итоге придется использовать библиотеку произвольных чисел, ваш номер оказался достаточно маленьким для 64-разрядных, но слишком большим для 32-разрядных.
источник
В рубине:
Печатает
500000000500000000
, но занимает 4 минуты на моем 2,6 ГГц Intel i7.У Magnuss и Jaunty есть гораздо более подходящее решение для Ruby:
Чтобы запустить тест:
источник
Я использую node-bigint для больших целых чисел:
https://github.com/substack/node-bigint
Это не так быстро, как то, что может использовать нативный 64-битный материал для этого точного теста, но если вы получите большее число, чем 64-битный, он использует libgmp под капотом, который является одной из самых быстрых библиотек произвольной точности.
источник
берут в рубине целую вечность, но дает правильный ответ:
источник
Чтобы получить правильный результат в php, я думаю, вам нужно использовать математические операторы BC: http://php.net/manual/en/ref.bc.php
Вот правильный ответ в Scala. Вы должны использовать длинные, иначе вы переполните число:
источник
На самом деле есть крутой трюк с этой проблемой.
Предположим, что это было 1-100 вместо.
1 + 2 + 3 + 4 + ... + 50 +
100 + 99 + 98 + 97 + ... + 51
= (101 + 101 + 101 + 101 + ... + 101) = 101 * 50
Формула:
Для N = 100: Выход = N / 2 * (N + 1)
Для N = 1e9: Выход = N / 2 * (N + 1)
Это намного быстрее, чем перебирать все эти данные. Ваш процессор поблагодарит вас за это. И вот интересная история, касающаяся этой самой проблемы:
http://www.jimloy.com/algebra/gauss.htm
источник
Это дает правильный результат в PHP путем принудительного целочисленного приведения.
источник
Common Lisp является одним из самых быстрых интерпретируемых * языков и по умолчанию обрабатывает произвольно большие целые числа. Это занимает около 3 секунд с SBCL :
источник
У меня недостаточно репутации, чтобы комментировать ответ на Common Lisp @ postfuturist, но его можно оптимизировать для выполнения через ~ 500 мс с SBCL 1.1.8 на моей машине:
источник
Ракетка v 5.3.4 (MBP; время в мс):
источник
Прекрасно работает в Rebol:
При этом использовался Rebol 3, который, несмотря на 32-битную компиляцию, использует 64-битные целые числа (в отличие от Rebol 2, который использовал 32-битные целые числа)
источник
Я хотел посмотреть, что произошло в CF Script
Я получил 5.00000000067E + 017
Это был довольно аккуратный эксперимент. Я вполне уверен, что мог бы закодировать это немного лучше, приложив больше усилий.
источник
ActivePerl v5.10.1 на 32-битных окнах, Intel Core2duo 2.6:
Результат: 5.00000000067109e + 017 за 5 минут.
С «use bigint» скрипт работал два часа, и работал бы больше, но я его остановил. Слишком медленно.
источник
Для полноты в Clojure (красиво, но не очень эффективно):
источник
AWK:
выдает тот же неверный результат, что и PHP:
Кажется, что AWK использует числа с плавающей запятой, когда числа действительно велики, поэтому, по крайней мере, ответ - правильный порядок.
Тестовые прогоны:
источник
Категория другой интерпретируемый язык:
Tcl:
Если используется Tcl 8.4 или старше, это зависит от того, был ли он скомпилирован с 32- или 64-разрядным. (8.4 это конец жизни).
Если вы используете Tcl 8.5 или новее с произвольными большими целыми числами, он будет отображать правильный результат.
Я поместил тест в proc, чтобы получить его скомпилированным байтом.
источник
Для кода PHP ответ здесь :
источник
порт:
Результаты в
500000000500000000
. (на обоих windows / mingw / x86 и osx / clang / x64)источник
Эрланг работает:
источник
Забавно, PHP 5.5.1 выдает 499999999500000000 (через ~ 30 с), в то время как Dart2Js выдает 500000000067109000 (что и следовало ожидать, поскольку выполняется JS). CLI Dart дает правильный ответ ... мгновенно.
источник
Эрланг тоже дает ожидаемый результат.
sum.erl:
И используя это:
источник
Болтовня:
источник