Почему ** 2! = A * a для некоторых чисел? - PullRequest
0 голосов
/ 01 июня 2018
$ python --version
Python 2.7.15

$ type test.py
import random

while True:
    a = random.uniform(0, 1)
    b = a ** 2
    c = a * a
    if b != c:
        print "a = {}".format(a)
        print "a ** 2 = {}".format(b)
        print "a * a = {}".format(c)
        break

$ python test.py
a = 0.145376687586
a ** 2 = 0.0211343812936
a * a = 0.0211343812936

Мне удалось воспроизвести это только на сборке Python для Windows, если быть точным: Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32.На моем Arch Linux боксе Python (Python 2.7.15 (default, May 1 2018, 20:16:04) [GCC 7.3.1 20180406] on linux2), похоже, цикл не прерывается, указывая на то, что там сохраняется инвариант a**2 = a * a.

Что здесь происходит?Я знаю, что поплавки IEEE сопровождаются множеством заблуждений и особенностей (например, this не отвечает на мой вопрос), но я не вижу, какая часть спецификации или какая реализация ** может допустить это.

Для устранения дублирования пометки: это, скорее всего, не напрямую математическая задача IEEE с плавающей запятой, а скорее проблема реализации оператора **.Следовательно, это не дубликат вопросов, которые задают только вопросы с плавающей запятой, такие как точность или ассоциативность.

Ответы [ 2 ]

0 голосов
/ 01 июня 2018

a ** 2 использует функцию с плавающей запятой (например, ту, что вы можете найти в стандартной C математической библиотеке), которая способна поднять любое число до любой степени.

a * a просто умножает один раз , это больше подходит для этого случая и не подвержено ошибкам точности (даже более верно для целых чисел), как a ** 2.

Дляa с плавающей запятой, если вы хотите повысить до, скажем, 5, используя

a * a * a * a * a

, вам будет лучше использовать a**5, потому что повторное умножение теперь склонно к плавающей запятойошибка накопления, и она намного медленнее.

a ** b более интересен, когда b большой, например, потому что он более эффективен.Но точность может отличаться, потому что он использует алгоритм с плавающей запятой.

0 голосов
/ 01 июня 2018

Python опирается на базовую платформу для своей арифметики с плавающей точкой.Я предполагаю, что оператор Python ** использует реализацию pow (как в C) (подтверждено user2357112 со ссылкой на исходный код Python 2.7.15 ).

Как правило, pow реализуется с использованием (приближений) логарифмов и экспонент, частично.Это необходимо, поскольку pow поддерживает нецелые аргументы.(Конечно, эта общая реализация не исключает специализаций для подмножеств своего домена.)

Реализация Microsoft pow, как известно, не очень хороша.Следовательно, для pow(a, 2) он может возвращать результат, не равный a*a.

...