Python zmiennoprzecinkowy arbitralna precyzja dostępna?

Dla zabawy i ponieważ było to naprawdę łatwe, napisałem krótki program do generowania liczb szczepionych , ale ze względu na problemy z precyzją zmiennoprzecinkową nie znajduję niektórych większych przykładów.

def isGrafting(a):
  for i in xrange(1, int(ceil(log10(a))) + 2):
    if a == floor((sqrt(a) * 10**(i-1)) % 10**int(ceil(log10(a)))):
      return 1

a = 0
while(1):
  if (isGrafting(a)):
    print "%d %.15f" % (a, sqrt(a))
  a += 1

Ten kod pomija co najmniej jeden znany numer szczepienia. 9999999998 => 99999.99998999999999949999999994999999999374999999912... wydaje się, że po pomnożeniu przez 10**5 spada dodatkowa precyzja.

>>> a = 9999999998
>>> sqrt(a)
99999.99999
>>> a == floor((sqrt(a) * 10**(5)) % 10**int(ceil(log10(a))))
False
>>> floor((sqrt(a) * 10**(5)) % 10**int(ceil(log10(a))))
9999999999.0
>>> print "%.15f" % sqrt(a)
99999.999989999996615
>>> print "%.15f" % (sqrt(a) * 10**5)
9999999999.000000000000000

Więc napisałem krótki program C++, aby sprawdzić, czy to mój procesor obcinający liczbę zmiennoprzecinkową, czy python jakoś.

#include <cstdio>
#include <cmath>
#include <stdint.h>

int main()
{
  uint64_t a = 9999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e4, sqrt((double)a)*1e5, sqrt((double)a)*1e6);
  a = 999999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e5, sqrt((double)a)*1e6, sqrt((double)a)*1e7);
  a = 99999999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e6, sqrt((double)a)*1e7, sqrt((double)a)*1e8);
  return 0;
}

Które wyjście:

9999999998 99999.999989999996615 999999999.899999976158142 9999999999.000000000000000 99999999990.000000000000000
999999999998 999999.999998999992386 99999999999.899993896484375 999999999999.000000000000000 9999999999990.000000000000000
99999999999998 9999999.999999899417162 9999999999999.900390625000000 99999999999999.000000000000000 999999999999990.000000000000000

Więc wygląda na to, że jestem mocno na granicy precyzji zmiennoprzecinkowej i procesor odcina pozostałe bity, ponieważ myśli, że pozostała różnica to błąd zmiennoprzecinkowy. Czy jest jakiś sposób na obejście tego pod Pythonem? Czy muszę przejść do C i używać GMP czy coś?

Author: OmnipotentEntity, 2012-07-17

5 answers

W bibliotece standardowej,decimal moduł może być tym, czego szukasz. Ponadto, znalazłem mpmath być bardzo pomocne. Dokumentacja ma również wiele świetnych przykładów (niestety mój komputer biurowy nie ma zainstalowanego mpmath; w przeciwnym razie zweryfikowałbym kilka przykładów i zamieściłbym je).

Jedno zastrzeżenie o decimal Moduł. Moduł zawiera kilka wbudowanych funkcji do prostych operacji matematycznych (np.]}), jednak wyniki z tych funkcji mogą nie zawsze pasować do odpowiedniej funkcji w math lub innych modułach o wyższych precyzjach (chociaż mogą być bardziej dokładne). Na przykład,

from decimal import *
import math

getcontext().prec = 30
num = Decimal(1) / Decimal(7)

print("   math.sqrt: {0}".format(Decimal(math.sqrt(num))))
print("decimal.sqrt: {0}".format(num.sqrt()))

W Pythonie 3.2.3, to wyprowadza dwie pierwsze linie

   math.sqrt: 0.37796447300922719758631274089566431939601898193359375
decimal.sqrt: 0.377964473009227227214516536234
actual value: 0.3779644730092272272145165362341800608157513118689214

Które, jak stwierdzono, nie jest dokładnie to, czego można się spodziewać, i widać, że im wyższa precyzja, tym mniej wyniki się zgadzają. Zauważ, że moduł decimal ma większą dokładność w tym przykładzie, ponieważ bardziej pasuje rzeczywista wartość .

 34
Author: Ricardo Altamirano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-05 09:19:47

Dla tego konkretnego problemu, decimal jest świetnym rozwiązaniem, ponieważ przechowuje cyfry dziesiętne jako krotki!

>>> a = decimal.Decimal(9999999998)
>>> a.as_tuple()
DecimalTuple(sign=0, digits=(9, 9, 9, 9, 9, 9, 9, 9, 9, 8), exponent=0)

Ponieważ szukasz właściwości, która jest najbardziej naturalnie wyrażona w notacji dziesiętnej, trochę głupio jest używać reprezentacji binarnej. Strona Wikipedii, do której linkowałeś, nie wskazywała, ile "cyfr nieszczepiających" może pojawić się przed rozpoczęciem "cyfr przeszczepiających" , więc pozwala to określić:

>>> def isGrafting(dec, max_offset=5):
...     dec_digits = dec.as_tuple().digits
...     sqrt_digits = dec.sqrt().as_tuple().digits
...     windows = [sqrt_digits[o:o + len(dec_digits)] for o in range(max_offset)]
...     return dec_digits in windows
... 
>>> isGrafting(decimal.Decimal(9999999998))
True
>>> isGrafting(decimal.Decimal(77))
True

Myślę, że jest duża szansa, że wynik Decimal.sqrt() będzie dokładniejsze, przynajmniej w tym przypadku, niż wynik math.sqrt() z powodu konwersji między reprezentacją binarną a reprezentacją dziesiętną. Rozważmy na przykład:

>>> num = decimal.Decimal(1) / decimal.Decimal(7)
>>> decimal.Decimal(math.sqrt(num) ** 2) * 7
Decimal('0.9999999999999997501998194593')
>>> decimal.Decimal(num.sqrt() ** 2) * 7
Decimal('1.000000000000000000000000000')
 8
Author: senderle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-17 13:46:18

Możesz spróbować z Decimal zamiast floatingpoint.

 7
Author: f p,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-17 13:04:23

Python nie ma wbudowanych pływaków o arbitralnej precyzji, ale istnieją pakiety Pythona innych firm, które używają GMP: gmpy i PyGMP .

 5
Author: Ned Batchelder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-17 13:02:49

Użyj decimal, (tutaj jest jaśniejszy przykład):

>>> 2.3-2.2
0.09999999999999964
>>> from decimal import Decimal
>>> Decimal('2.3')-Decimal('2.2')
Decimal('0.1')
>>> float(Decimal('2.3')-Decimal('2.2'))
0.1
>>> 
 0
Author: U9-Forward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-18 01:29:41