2012-07-17 26 views
30

Sadece eğlence için ve çok kolay olduğu için, Grafting numbers oluşturmak için kısa bir program yazdım, ancak kayan nokta hassaslığı sorunları nedeniyle daha büyük örneklerden bazılarını bulamıyor.Python kayan nokta keyfi hassas kullanılabilir mi?

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 

Bu kod bilinen en az bir Grafting numarası eksik. 9999999998 => 99999.99998999999999949999999994999999999374999999912...10**5 ile çarpıldıktan sonra ekstra hassasiyet düşüyor gibi görünüyor.

>>> 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 

Yani bir şekilde kayan nokta numarası veya python kısaltılıyor benim işlemci olup olmadığını görmek için kısa bir C++ programı yazdım. çıkışı

#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; 
} 

: Ben koşuyorum gibi

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 

yüzden duyarlılığında sınırları karşı sert bakar ve düşündüğü için CPU kalan bitleri kesiyor kalan fark olduğunu kayan nokta hatasıdır. Python altında bunun üzerinde çalışmanın bir yolu var mı? Yoksa C'ye geçmek ve GMP'yi mi kullanmalıyım?

+1

rasyonel sayı kesin aritmetik gerçekleştirmek için, [ 'fractions' modül] (https://docs.python.org/3/library/fractions.html) kullanılabilir . Mpmath için – jfs

cevap

31

Standart kütüphanede, aradığınız şey decimal modülü olabilir. Ayrıca, oldukça yardımcı olmak için mpmath'u buldum. documentation'un da birçok harika örneği var (maalesef ofis bilgisayarımda mpmath yüklü değil, aksi halde birkaç örneği doğrulayıp gönderirim).

decimal modülüyle ilgili bir uyarı. Modül basit matematiksel işlemler için birçok yerleşik fonksiyon içerir (ör. sqrt), ancak bu fonksiyonların sonuçları her zaman ilgili fonksiyonu math veya diğer modüllerdeki daha yüksek hassasiyetle eşleştirmeyebilir (daha doğru olabilir). Örneğin,

Python 3.2.3, bu ilk iki satırı belirtildiği gibi
math.sqrt: 0.37796447300922719758631274089566431939601898193359375 
decimal.sqrt: 0.377964473009227227214516536234 
actual value: 0.3779644730092272272145165362341800608157513118689214 

verir
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())) 

, beklediğiniz tam olarak ne değildir ve siz görebilirsiniz yüksek Kesinlik, sonuç ne kadar az eşleşir. actual value ile daha yakından eşleştiğinden, decimal modülünün bu örnekte daha doğru olduğunu unutmayın.

+2

+1. Ondalık sayıları kullanmayla ilgili problem, ondalık nesneler üzerinde matematik işlevlerinde çok fazla şey yapamayacağınızdır, bu yüzden sadece oyun oynamak oldukça kısıtlayıcıdır. – DSM

+0

@DSM Kabul ediyorum. Oldukça basit sorunlar için sadece 'mpmath' kullandım ama yine de iyi ve sağlam bir paket buldum. –

+1

Sadece açık olmak gerekirse - 'math.sqrt' vs. Decimal.sqrt() 'testinde,' math.sqrt' tarafından üretilen sonucun, ikili-nedeniyle, _less_ doğru olduğundan eminim -decimal dönüşüm. "Decimal.Decimal (math.sqrt (num) ** 2) * 7 'çıktısını" decimal.Decimal (num.sqrt() ** 2) * 7' çıktısı olarak düşünün. – senderle

7

Kayan nokta yerine Decimal ile deneyebilirsiniz.

5

Python'da yerleşik olarak rastgele hassas bir şamandıra yoktur, ancak GMP kullanan üçüncü taraf Python paketleri vardır: gmpy ve PyGMP.

8

Bu sorun için decimal gitmek için harika bir yoldur, çünkü ondalık basamakları tuples olarak depolar!

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

en doğal ondalık gösterimle ifade edilir bir özellik arıyorsanız bu yana, ikili gösterimini kullanmak biraz saçma.wikipedia sayfası "aşılama basamak" başlamadan önce "olmayan aşılama rakam" görünebilir kaç işaret etmemiştir bağlantılı, bu yüzden bu belirttiğiniz etmenizi sağlar:

>>> 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 

Ben iyi bir şans sonucu olduğunu düşünüyorum Decimal.sqrt(), en azından bunun için, ikili temsil ve ondalık gösterimler arasındaki dönüşüm nedeniyle math.sqrt() sonucundan daha doğru olacaktır. örneğin, aşağıdaki göz önünde bulundurun:

>>> 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')