Sayıları daha küçük parçalar olarak ele almak, yeterli depolama ve algoritmalar meselesidir. Bir derleyicinizin int
yalnızca 0'dan 99'a kadar olabileceği ve 999999'a kadar olan sayıları işlemek istediğinizi varsayalım (burada basit tutmak için yalnızca pozitif sayılar için endişeleneceğiz).
Bunu, her bir numaraya üç int
s vererek ve toplama, çıkarma ve diğer temel işlemler için ilkokulda öğrendiğiniz (sahip olmanız gereken) aynı kuralları kullanarak yaparsınız.
Rasgele bir hassas kitaplıkta, sayılarımızı temsil etmek için kullanılan temel türlerin sayısında sabit bir sınır yoktur, tam da belleğin tutabildiği şey.
Ekleme, örneğin 123456 + 78
::
12 34 56
78
-- -- --
12 35 34
En önemsiz uçtan çalışmak:
- ilk taşıma = 0.
- 56 + 78 + 0 taşıma = 134 = 34 1 taşıma ile
- 34 + 00 + 1 taşıma = 35 = 35 ile 0 taşıma
- 12 + 00 + 0 taşıma = 12 = 12 ile 0 taşıma
Aslında bu, eklemenin genellikle CPU'nuzdaki bit seviyesinde nasıl çalıştığıdır.
Çıkarma benzerdir (taban türünün çıkarılması ve taşıma yerine ödünç alma kullanılarak), çarpma, tekrarlanan eklemelerle (çok yavaş) veya çapraz ürünlerle (daha hızlı) yapılabilir ve bölme daha zordur, ancak sayıların kaydırılması ve çıkarılmasıyla yapılabilir. dahil (çocukken öğreneceğiniz uzun bölüm).
Aslında, karesi alındığında bir tamsayıya sığabilen on'un maksimum güçlerini kullanarak bu tür şeyler yapmak için kitaplıklar yazdım (iki int
s'yi birlikte çarparken taşmayı önlemek için , örneğin 16 bitin int
0 ile 99 arasında sınırlanması gibi) karesi alınırken 9,801 (<32,768) veya int
99,980,001 (<2,147,483,648) oluşturmak için 0 ila 9,999 kullanarak 32 bit üretin ve bu da algoritmaları büyük ölçüde kolaylaştırdı.
Dikkat edilmesi gereken bazı püf noktaları.
1 / Numaraları eklerken veya çarparken, gereken maksimum alanı önceden ayırın ve daha sonra çok fazla bulursanız azaltın. Örneğin, iki adet 100 "basamaklı" (basamak bir int
sayıdır) sayı eklemek size asla 101 basamaktan fazlasını vermez. 12 basamaklı bir sayıyı 3 basamaklı bir sayı ile çarptığınızda hiçbir zaman 15 basamaktan fazlasını oluşturmayacaksınız (basamak sayılarını ekleyin).
2 / Daha fazla hız için, sayıları yalnızca kesinlikle gerekliyse normalleştirin (gereken depolamayı azaltın) - kütüphanem bunu ayrı bir arama olarak yaptı, böylece kullanıcı hız ve depolama sorunları arasında karar verebilir.
3 / Pozitif ve negatif bir sayının toplanması çıkarma işlemidir ve negatif bir sayının çıkarılması, eşdeğer pozitifin toplanmasıyla aynıdır. İşaretleri ayarladıktan sonra toplama ve çıkarma yöntemlerinin birbirini çağırmasını sağlayarak oldukça fazla kod kaydedebilirsiniz.
4 / Büyük sayıları küçük sayılardan çıkarmaktan kaçının çünkü her zaman aşağıdaki gibi sayılarla karşılaşacaksınız:
10
11-
-- -- -- --
99 99 99 99 (and you still have a borrow).
Bunun yerine, 11'den 10'u çıkarın, sonra onu olumsuzlayın:
11
10-
--
1 (then negate to get -1).
İşte bunu yapmak zorunda olduğum kütüphanelerden birinin yorumları (metne dönüştürülmüş). Kodun kendisi maalesef telif hakkıyla korunmaktadır, ancak dört temel işlemi idare etmek için yeterli bilgiyi seçmeniz mümkün olabilir. Aşağıdakileri varsayalım -a
ve -b
negatif sayıları temsil eder a
ve b
sıfır veya pozitif sayılardır.
İçin ek işaretler farklı ise, olumsuzlama kullanımı çıkarma:
-a + b becomes b - a
a + -b becomes a - b
İçin çıkarma , işaretler farklı ise, olumsuzlama kullanımı ilavesi:
a - -b becomes a + b
-a - b becomes -(a + b)
Ayrıca küçük sayıları büyük sayılardan çıkardığımızdan emin olmak için özel işlem:
small - big becomes -(big - small)
Çarpma , aşağıdaki gibi giriş düzeyinde matematik kullanır:
475(a) x 32(b) = 475 x (30 + 2)
= 475 x 30 + 475 x 2
= 4750 x 3 + 475 x 2
= 4750 + 4750 + 4750 + 475 + 475
Bunun elde edilme yolu, 32 basamaktan her birinin bir seferde (geriye doğru) çıkarılmasını ve ardından sonuca eklenecek bir değeri (başlangıçta sıfır) hesaplamak için add işlevinin kullanılmasını içerir.
ShiftLeft
ve ShiftRight
işlemler a'yı LongInt
sarma değeriyle hızlıca çarpmak veya bölmek için kullanılır ("gerçek" matematik için 10). Yukarıdaki örnekte, 950 elde etmek için 475'i sıfıra 2 kez (32'nin son rakamı) ekliyoruz (sonuç = 0 + 950 = 950).
Sonra 4750'ye kaydırarak 475'i bıraktık ve 3'ü elde etmek için sağa 32 kaydırdık. 14250'yi elde etmek için 4750'yi sıfıra 3 kez ekledikten sonra 15200 elde etmek için 950'nin sonucuna ekleyin.
4750'yi 47500 almak için sola kaydırın, 0 almak için sağa kaydırın. Sağa kaydırılan 32 şimdi sıfır olduğundan, işimiz bitti ve aslında 475 x 32, 15200'e eşittir.
Bölme de zordur ancak erken aritmetiğe dayanır ("içine girer" için "gazinta" yöntemi). Aşağıdaki uzun bölmeyi düşünün 12345 / 27
:
457
+-------
27 | 12345 27 is larger than 1 or 12 so we first use 123.
108 27 goes into 123 4 times, 4 x 27 = 108, 123 - 108 = 15.
---
154 Bring down 4.
135 27 goes into 154 5 times, 5 x 27 = 135, 154 - 135 = 19.
---
195 Bring down 5.
189 27 goes into 195 7 times, 7 x 27 = 189, 195 - 189 = 6.
---
6 Nothing more to bring down, so stop.
Bu nedenle 12345 / 27
bir 457
geri kalanı ile 6
. Doğrulayın:
457 x 27 + 6
= 12339 + 6
= 12345
Bu, 12345 segmentlerini 27'ye eşit veya daha büyük olana kadar teker teker aşağı çekmek için bir aşağı çekme değişkeni (başlangıçta sıfır) kullanılarak gerçekleştirilir.
Sonra 27'nin altına inene kadar bundan 27 çıkarırız - çıkarma sayısı en üst satıra eklenen segmenttir.
Aşağı indirilecek daha fazla segment kalmadığında, sonucumuz var.
Bunların oldukça basit algoritmalar olduğunu unutmayın. Sayılarınız özellikle büyük olacaksa, karmaşık aritmetik yapmanın çok daha iyi yolları vardır. GNU Çoklu Duyarlı Aritmetik Kitaplığı gibi bir şeye bakabilirsiniz - bu benim kitaplıklarımdan önemli ölçüde daha iyi ve daha hızlıdır.
Oldukça talihsiz bir yanlışlığa sahiptir, çünkü hafızası biterse basitçe çıkacaktır (bence genel amaçlı bir kütüphane için oldukça ölümcül bir kusurdur), ancak bunun geçmişine bakabilirseniz, yaptığı işte oldukça iyidir.
Lisanslama amacıyla kullanamıyorsanız (veya uygulamanızın görünürde bir neden olmadan çıkmasını istemiyorsanız), en azından algoritmaları oradan kendi kodunuza entegre etmek için alabilirsiniz.
Ayrıca, MPIR'daki (bir GMP çatalı) gövdelerin potansiyel değişikliklerle ilgili tartışmalara daha uygun olduğunu buldum - daha geliştirici dostu bir grup gibi görünüyorlar.