ทุกอย่างเป็นเรื่องของการจัดเก็บและอัลกอริทึมที่เพียงพอในการถือว่าตัวเลขเป็นส่วนเล็ก ๆ สมมติว่าคุณมีคอมไพเลอร์ที่int
สามารถเป็น 0 ถึง 99 เท่านั้นและคุณต้องการจัดการตัวเลขได้ถึง 999999 (เราจะกังวลเฉพาะตัวเลขบวกที่นี่เพื่อให้มันง่าย)
คุณทำได้โดยให้เลขสามตัวแต่ละตัวint
และใช้กฎเดียวกันกับที่คุณ (ควรจะได้) เรียนกลับมาในโรงเรียนประถมเพื่อการบวกการลบและการดำเนินการพื้นฐานอื่น ๆ
ในไลบรารีความแม่นยำตามอำเภอใจไม่มีการ จำกัด จำนวนประเภทฐานที่แน่นอนที่ใช้แทนตัวเลขของเราหน่วยความจำใดก็ได้
นอกจากนี้ตัวอย่างเช่น123456 + 78
:
12 34 56
78
-- -- --
12 35 34
การทำงานจากจุดสิ้นสุดที่สำคัญน้อยที่สุด:
- เริ่มต้นดำเนินการ = 0
- 56 + 78 + 0 พกพา = 134 = 34 พร้อม 1 พก
- 34 + 00 + 1 พก = 35 = 35 กับ 0 พก
- 12 + 00 + 0 พกพา = 12 = 12 กับ 0 พก
นี่คือความจริงแล้วการเพิ่มโดยทั่วไปทำงานอย่างไรในระดับบิตภายใน CPU ของคุณ
การลบจะคล้ายกัน (โดยใช้การลบของประเภทฐานและยืมแทนการพกพา) การคูณสามารถทำได้ด้วยการเพิ่มซ้ำ (ช้ามาก) หรือผลคูณไขว้ (เร็วกว่า) และการหารจะยุ่งยากกว่า แต่สามารถทำได้โดยการเลื่อนและการลบตัวเลข เกี่ยวข้อง (ส่วนยาวที่คุณเคยเรียนรู้เมื่อตอนเป็นเด็ก)
ฉันได้เขียนไลบรารีเพื่อทำสิ่งเหล่านี้โดยใช้กำลังสูงสุดสิบที่สามารถใส่เป็นจำนวนเต็มเมื่อยกกำลังสองได้ (เพื่อป้องกันการล้นเมื่อคูณสองint
s เข้าด้วยกันเช่น 16 บิตint
ถูก จำกัด ไว้ที่ 0 ถึง 99 ถึง สร้าง 9,801 (<32,768) เมื่อกำลังสองหรือ 32 บิตint
โดยใช้ 0 ถึง 9,999 เพื่อสร้าง 99,980,001 (<2,147,483,648)) ซึ่งทำให้อัลกอริทึมง่ายขึ้นมาก
เทคนิคบางอย่างที่ต้องระวัง
1 / เมื่อเพิ่มหรือคูณตัวเลขให้จัดสรรพื้นที่สูงสุดที่จำเป็นไว้ล่วงหน้าแล้วลดในภายหลังหากคุณพบว่ามันมากเกินไป ตัวอย่างเช่นการเพิ่มตัวเลข 100- "หลัก" สองตัว (โดยที่หลักคือ an int
) จะไม่ทำให้คุณเกิน 101 หลัก การคูณตัวเลข 12 หลักด้วยตัวเลข 3 หลักจะไม่ทำให้เกิดตัวเลขเกิน 15 หลัก (บวกจำนวนหลัก)
2 / สำหรับความเร็วที่เพิ่มขึ้นให้ทำให้ปกติ (ลดพื้นที่จัดเก็บข้อมูลที่จำเป็นสำหรับ) ตัวเลขเฉพาะในกรณีที่จำเป็นจริงๆ - ห้องสมุดของฉันมีสิ่งนี้เป็นการโทรแยกกันเพื่อให้ผู้ใช้สามารถตัดสินใจระหว่างความเร็วและความกังวลในการจัดเก็บ
3 / การบวกจำนวนบวกและลบคือการลบและการลบจำนวนลบจะเหมือนกับการบวกบวกที่เท่ากัน คุณสามารถบันทึกโค้ดได้เล็กน้อยโดยให้วิธีการบวกและลบเรียกหากันหลังจากปรับสัญญาณ
4 / หลีกเลี่ยงการลบตัวเลขจำนวนมากออกจากตัวเลขขนาดเล็กเนื่องจากคุณมักจะลงท้ายด้วยตัวเลขเช่น:
10
11-
-- -- -- --
99 99 99 99 (and you still have a borrow).
ให้ลบ 10 จาก 11 แทนแล้วลบมัน:
11
10-
--
1 (then negate to get -1).
นี่คือความคิดเห็น (กลายเป็นข้อความ) จากหนึ่งในห้องสมุดที่ฉันต้องทำเพื่อ น่าเสียดายที่รหัสนั้นมีลิขสิทธิ์ แต่คุณอาจสามารถเลือกข้อมูลที่เพียงพอเพื่อจัดการกับการทำงานพื้นฐานทั้งสี่อย่างได้ สมมติในสิ่งต่อไปนี้-a
และ-b
แทนจำนวนลบและa
และb
เป็นศูนย์หรือจำนวนบวก
สำหรับนอกจากนี้ถ้าสัญญาณที่แตกต่างกันใช้ลบของการปฏิเสธ:
-a + b becomes b - a
a + -b becomes a - b
สำหรับการลบหากสัญญาณแตกต่างกันให้ใช้การเพิ่มการปฏิเสธ:
a - -b becomes a + b
-a - b becomes -(a + b)
นอกจากนี้การจัดการพิเศษเพื่อให้แน่ใจว่าเรากำลังลบจำนวนน้อยออกจากจำนวนมาก:
small - big becomes -(big - small)
การคูณใช้คณิตศาสตร์ระดับเริ่มต้นดังนี้:
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
วิธีที่บรรลุเป้าหมายนี้เกี่ยวข้องกับการแยกแต่ละหลัก 32 ทีละครั้ง (ถอยหลัง) จากนั้นใช้การเพิ่มเพื่อคำนวณค่าที่จะเพิ่มให้กับผลลัพธ์ (เริ่มต้นเป็นศูนย์)
ShiftLeft
และShiftRight
การดำเนินการใช้เพื่อคูณหรือหาร a อย่างรวดเร็วLongInt
ด้วยค่าการห่อ (10 สำหรับคณิตศาสตร์ "จริง") ในตัวอย่างด้านบนเราบวก 475 เป็นศูนย์ 2 ครั้ง (ตัวเลขสุดท้ายของ 32) เพื่อให้ได้ 950 (ผลลัพธ์ = 0 + 950 = 950)
จากนั้นเราเลื่อนซ้าย 475 เพื่อรับ 4750 และเลื่อนขวา 32 เพื่อรับ 3 เพิ่ม 4750 เป็นศูนย์ 3 ครั้งเพื่อรับ 14250 จากนั้นเพิ่มผลของ 950 เพื่อรับ 15200
เลื่อนไปทางซ้าย 4750 เพื่อรับ 47500 กะขวา 3 เพื่อรับ 0 เนื่องจากตอนนี้เลื่อนขวา 32 เป็นศูนย์เราจึงเสร็จสิ้นและในความเป็นจริง 475 x 32 เท่ากับ 15200
การหารก็ยุ่งยากเช่นกัน แต่ขึ้นอยู่กับเลขคณิตในช่วงต้น (วิธี "gazinta" สำหรับ "ไปสู่") พิจารณาการหารยาวต่อไปนี้สำหรับ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.
ดังนั้นจึง12345 / 27
เป็นส่วนที่เหลือ457
6
ตรวจสอบ:
457 x 27 + 6
= 12339 + 6
= 12345
สิ่งนี้ถูกนำไปใช้โดยใช้ตัวแปร draw-down (เริ่มต้นเป็นศูนย์) เพื่อลดส่วนของ 12345 ทีละส่วนจนกว่าจะมากกว่าหรือเท่ากับ 27
จากนั้นเราก็ลบ 27 จากนั้นจนกว่าเราจะได้ต่ำกว่า 27 - จำนวนการลบคือส่วนที่เพิ่มในบรรทัดบนสุด
เมื่อไม่มีกลุ่มที่จะนำมาลงอีกเราก็ได้ผลลัพธ์ของเรา
โปรดทราบว่านี่เป็นอัลกอริทึมพื้นฐานที่ค่อนข้างดี มีวิธีที่ดีกว่ามากในการคำนวณเลขคณิตเชิงซ้อนหากตัวเลขของคุณมีขนาดใหญ่เป็นพิเศษ คุณสามารถดูบางอย่างเช่นGNU Multiple Precision Arithmetic Library - มันดีกว่าและเร็วกว่าไลบรารีของฉันเอง
มันมีความผิดพลาดที่ค่อนข้างน่าเสียดายที่มันจะออกไปหากหน่วยความจำหมด (ข้อบกพร่องที่ค่อนข้างร้ายแรงสำหรับไลบรารีวัตถุประสงค์ทั่วไปในความคิดของฉัน) แต่ถ้าคุณสามารถมองผ่านไปได้ก็ค่อนข้างดีในสิ่งที่มันทำ
หากคุณไม่สามารถใช้เนื่องจากเหตุผลด้านการอนุญาต (หรือเพราะคุณไม่ต้องการให้แอปพลิเคชันของคุณเพิ่งออกโดยไม่มีเหตุผลที่ชัดเจน) อย่างน้อยคุณก็สามารถรับอัลกอริทึมจากที่นั่นเพื่อรวมเข้ากับโค้ดของคุณเองได้
ฉันยังพบว่า bods ที่MPIR (ส่วนแยกของ GMP) นั้นเอื้อต่อการพูดคุยเกี่ยวกับการเปลี่ยนแปลงที่อาจเกิดขึ้นได้มากขึ้นซึ่งดูเหมือนจะเป็นกลุ่มที่เป็นมิตรกับนักพัฒนามากกว่า