Невероятный math.pow

Jul 28, 2012 22:27


Задачка для младших инженеров-программистов электронно-вычислительных машин.

C:

#include #include int main() { printf("%d\n", (pow(43, 10) == 21611482313284249ll)); return 0; }

Python:

import math print(math.pow(43, 10) == 21611482313284249)

Вопрос: что выведется и почему?

нечеловеческие языки, задачка

Leave a comment

Comments 16

salas July 29 2012, 00:59:48 UTC
Питон не знаю, но очевидно, что pow одинаковый, а отличается дальнейшее обращение с его значением. На мой вкус, оно одинаково неправильное, потому что при применении ==/!= к floating point нужно по умолчанию выдавать хотя бы предупреждение, а уж приводить при этом тип налево или направо - дело десятое.

С другой стороны, на перле 43**10 вычисляется точно. И таки кто из них горбатый?
Upd: инструкцию и здесь надо читать, особенно редко применяемые места. Если не вспоминать про bignum и т.п. - то горб вполне есть, просто он на ещё больших числах. Что вполне в духе, да.

Reply

gegmopo4 July 29 2012, 07:10:59 UTC
Да, именно так всё и есть. См. «Incredible issue in math.pow».

В Питоне 43**10 тоже вычисляется точно. И даже 43**100.

Reply


qkowlew July 29 2012, 06:21:55 UTC
Когда-то давным давно начальный этап обучения программированию непременно содержал описание битового представления чисел, ограничений арифметики "в цифре" и т.п.

А тут даже константу написать - надо как минимум лезть в мануал по каждому из языков.
И сравнивать плавающие операцией == - ну вааащщщщщще.

В моей личной практике была отладка кода, в котором накапливающаяся ошибка округления в тупо написанном алгоритме с double float давала в сочетании с вычитанием больших чисел эффект "0=1" на всего лишь сотне поступивших в программу результатов измерения.

Reply

_winnie July 29 2012, 06:53:15 UTC
> И сравнивать плавающие операцией == - ну вааащщщщщще.
Пишущие на JavaScript, AWK, Lua, ... только так и сравнивают :)

Сравнивать с точностью до MY_EPSION?.. Сорри, что бы сравнить 43**10 и math.pow(43, 10) EPSILON должен быть не меньше чем 1.0 :)

Reply

gegmopo4 July 29 2012, 07:19:48 UTC
ll уже я добавил, иначе на 32-битных платформах терялась соль шутки.

У этой истории есть и мораль - не используйте плавучку в криптографии.

Reply

slobin July 29 2012, 09:00:39 UTC
Что добавляет конфузии из-за типографской путаницы "l" и "1". Особенно если не помнить твёрдо о существовании констант long long (кстати, а они в стандарте есть? не помню...).

... Sing, Maglor the Stranger, a ballade for me ...

Reply


slobin July 29 2012, 08:57:43 UTC
Ну, допустим, pow и прочие magic powers здесь вообще ни при чём. А что питон сравнивает длинные и плавающие НЕ приведением первых ко вторым -- я не знал. Но, в общем, логично -- так оно (сравнение) хотя бы транзитивно. Кстати, нашёл хороший запоминающийся пример:

>>> 1000000000000000000 == 1000000000000000000 + 0.0
True
>>> 1000000000000000001 == 1000000000000000001 + 0.0
False
>>> 1000000000000000002 == 1000000000000000002 + 0.0
False
>>> 1000000000000000003 == 1000000000000000003 + 0.0
False

Как вы думаете, когда будет следующий True? :-)

... В двадцать первом веке, в самом начале ...

Reply

slobin July 29 2012, 09:08:48 UTC
Вдогонку:

>>> N = 1000000000000000000
>>> [x for x in xrange(N, N + 1000) if x == float(x)]
Traceback (most recent call last):
File "", line 1, in
OverflowError: long int too large to convert to int

И они это называют языком высокого уровня? Срочно линяем отсюда куда-нибудь!

1> N = 1000000000000000000.
1000000000000000000
2> [X || X <- lists:seq(N, N + 1000), X == float(X)].
[1000000000000000000,1000000000000000128,
1000000000000000256,1000000000000000384,1000000000000000512,
1000000000000000640,1000000000000000768,1000000000000000896]

Ага, сравнение длинных с плавающими в эрланге и питоне устроено одинаково. :-)

... Целуются обычно так - берётся девушка красивая ...

Reply

gegmopo4 July 29 2012, 10:39:32 UTC
Это неправильный Питон, и у него неправильный xrange. В правильном Питоне:
>>> N = 1000000000000000000
>>> [x for x in range(N, N + 1000) if x == float(x)]
[1000000000000000000, 1000000000000000128, 1000000000000000256, 1000000000000000384, 1000000000000000512, 1000000000000000640, 1000000000000000768, 1000000000000000896]

Reply

gegmopo4 July 29 2012, 10:48:17 UTC
Комментарии к реализации сравнения чисел с плавающей точкой:
Comparison is pretty much a nightmare. When comparing float to float, we do it as straightforwardly (and long-windedly) as conceivable, so that, e.g., Python x == y delivers the same result as the platform C x == y when x and/or y is a NaN.

When mixing float with an integer type, there's no good uniform approach. Converting the double to an integer obviously doesn't work, since we may lose info from fractional bits. Converting the integer to a double also has two failure modes: (1) a long int may trigger overflow (too large to fit in the dynamic range of a C double); (2) even a C long may have more bits than fit in a C double (e.g., on a a 64-bit box long may have 63 bits of precision, but a C double probably has only 53), and then we can falsely claim equality when low-order integer bits are lost by coercion to double. So this part is painful too.

Reply


_winnie July 29 2012, 14:05:50 UTC
Меня сейчас добило ещё вот это:

>>> print pow(43, 10) == 43**10
True
>>> from math import pow
>>> pow(43, 10) == 43**10
False

Reply


molostoff September 11 2013, 15:57:31 UTC
а я не взирая на pow посмотрел на printf (где собстно %d)

таким образом в первой строке вывод будет 0 или 1, а во второй True или False

про формат чисел LL мне неизвестно. С требует явного указания типа здесь, т.к. оригинальный С long long int не имеет.

Reply

gegmopo4 September 11 2013, 17:59:09 UTC
long long int есть в C со стандарта 1999-го года.

Reply

molostoff September 11 2013, 18:21:37 UTC
(блин, дубль джва)

вы путаете стандарт языка и реализацию компилятора. вто время как вопрос был о том что выведется, а не как должно быть написано.

про ll я не помню. увы. что-то из виндовс. точнее в с99 не надо ll : 6.4.4.1Unsuffixed integer constants may have different types in C99 than in C89. Such
10 constants greater than LONG_MAX are of type unsigned long in C89, but are of
type long long in C99 (if long long has more range than long).
тоесть implicitly.

про явное указание типа: оно должно быть указано везде в вышеприведенном C, в частности %d заберет со стека int, в то время как FALSE или TRUE могут быть определены как 0L и 1L (литры наверно?)

а питон печатает boolean.

вопрос с вычислениями (pow) в части C целиком зависит от реализации компилятора, в частности есть ли в BSP операции c longlong int (== например), какие они, как их использует компилятор при компиляции (а ведь может) и тп. то есть на него нету ответа. по кр мере стандарт языка здесь не помжет.

Reply

gegmopo4 October 3 2013, 17:45:59 UTC
Клятый ЖЖ спрятал комментарий - в почте вижу, а в посте нет. Только через ЖЖ-шный инбокс удалось достать.

> вы путаете стандарт языка и реализацию компилятора.

Обычно компиляторы стараются реализовать стандарт. Отклонения считаются багами (или фичами).

> вто время как вопрос был о том что выведется, а не как должно быть написано.

Да, разумеется, результат платформозависим. Поэтому требовалось подумать, почему получен такой ответ. Угадать-то его с 50% вероятностью дело нехитрое.

> точнее в с99 не надо ll : 6.4.4.1

Вот этого я не знал. Да, это хорошо, это правильно.

> FALSE или TRUE могут быть определены как 0L и 1L

При чём здесь какие-то FALSE и TRUE? Они даже не упоминаются в коде.

> вопрос с вычислениями (pow) в части C целиком зависит от реализации компилятора

Да, это платформозависимый вопрос. Ответ на него зависит от числа разрядов мантиссы в double. Но практически на всех используемых сейчас платформах он однозначен.

Reply


Leave a comment

Up