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ś?
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ść .
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')
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.
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 .
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
>>>
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