วิธีที่เร็วที่สุดในการรับจำนวนเต็ม mod 10 และจำนวนเต็มหาร 10?


10

ถ้าฮาร์ดแวร์ไม่รองรับโมดูลัสหรือการหารมันต้องใช้รอบ CPU มากขึ้นในการจำลองโมดูลัส / การหารด้วยซอฟต์แวร์ มีวิธีใดที่เร็วกว่าในการคำนวณการหารและโมดูลัสถ้าตัวถูกดำเนินการเป็น 10?

ในโครงการของฉันฉันต้องคำนวณโมดูลัสจำนวนเต็ม 10 โดยเฉพาะฉันกำลังทำงานกับ PIC16F และต้องแสดงตัวเลขบนจอ LCD มีตัวเลข 4 หลักที่จะรองรับดังนั้นจึงมีการเรียกฟังก์ชั่นโมดูลัสและการหาร 4 ครั้ง (การนำซอฟต์แวร์ไปใช้) นั่นคือเหมือนดังต่อไปนี้:

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

มีพื้นที่อื่น ๆ ที่ใช้รหัสที่คล้ายกัน


ทำไมการโทรถึงสองสามโหล / วินาทีมีปัญหา? ฉันจะไม่รำคาญถ้าโครงการทำงานได้อย่างสมบูรณ์และปราศจากข้อผิดพลาด
Nick T

ฉันสังเกตเห็นว่าถ้าฉันแสดงหมายเลขต่อเนื่องในลูปหลักไม่ว่างการตอบสนองของปุ่มจะช้า นั่นคือเพื่อตรวจสอบว่ามีการกดปุ่มฉันต้องกดปุ่มนั้นอีกเล็กน้อย สิ่งนี้เกิดขึ้นเมื่อนาฬิกาของระบบกำลังทำงาน 32768 Hz
Donotalo

คุณกำลังใช้อินเทอร์รัปต์หรือไม่ ทำไมคุณถึงใช้ 32kHz xtal; โดยปกติคุณจะได้รับประสิทธิภาพการใช้พลังงานที่ลดลงหากคุณทำงานได้เร็วขึ้นและเข้าสู่โหมดสลีปเมื่อไม่มีการใช้งาน
Nick T

ฉันกำลังใช้อินเทอร์รัปต์ แต่เพียงเพื่ออัปเดตจอแสดงผลมันไม่คุ้มค่าที่จะเปลี่ยนมาใช้การแกว่งด้วยความเร็วสูง พลังงานที่ชาญฉลาด สำหรับโครงการของฉัน มันจะต้องใช้นาฬิกาความเร็วต่ำเกือบ 90% ของเวลาชีวิต
Donotalo

2
ขณะที่ทราบโดยทั่วไปหนังสือDelight แฮกเกอร์โดยเฮนรี่เอสวอร์เรนจูเนียร์เป็นแหล่งสำหรับฉลาดเล่ห์เหลี่ยมบิต twiddling ฉันค้นหาคำแนะนำการหารและมันไม่มีอะไรเลยสำหรับการหารด้วย 10 ที่เหนือกว่าคำตอบใด ๆ ด้านล่าง
RBerteig

คำตอบ:


11

ริไบนารี BCD ขั้นตอนวิธีการที่ผมใช้เวลาหลายปีที่ผ่านมาขึ้นอยู่กับหนึ่งพบที่นี่ ฉันใช้ไดรฟ์เวอร์แสดงผล BCD ภายนอก 7 เซกเมนต์ดังนั้นจึงสามารถเขียนผลลัพธ์ไปยังพอร์ตที่เหมาะสมโดยตรงเป็น BCD ที่อัดแน่นสำหรับเอาต์พุต

ค่อนข้างเร็วหากคุณมีตัวคูณฮาร์ดแวร์ใน PIC ฉันใช้ PIC18F97J60 หากคุณไม่มีตัวคูณฮาร์ดแวร์บน PIC ของคุณให้พิจารณาใช้ shift + เพิ่มสำหรับการคูณ

สิ่งนี้ใช้เวลาใน 16 บิต int ที่ไม่ได้ลงชื่อและส่งคืน BCD ที่บรรจุด้วยตัวเลข 5 หลักซึ่งสามารถแก้ไขและทำให้เร็วขึ้นเป็น 4 หลัก มันใช้ shift + เพิ่มเติมเพื่อประมาณหารด้วย 10 แต่กำหนดช่วงอินพุตที่ จำกัด ซึ่งแน่นอนสำหรับการใช้งานนี้ คุณอาจต้องการแพ็คผลลัพธ์ที่แตกต่างและสอดคล้องกับวิธีการใช้ผลลัพธ์ของคุณ

void intToPackedBCD( uint16_t n, uint8_t *digits ) {

    uint8_t d4, d3, d2, d1, d0, q;  //d4 MSD, d0 LSD

    d1 = (n>>4)  & 0xF;
    d2 = (n>>8)  & 0xF;
    d3 = (n>>12) & 0xF;

    d0 = 6*(d3 + d2 + d1) + (n & 0xF);
    q = (d0 * 0xCD) >> 11;
    d0 = d0 - 10*q;

    d1 = q + 9*d3 + 5*d2 + d1;
    q = (d1 * 0xCD) >> 11;
    d1 = d1 - 10*q;

    d2 = q + 2*d2;
    q = (d2 * 0x1A) >> 8;
    d2 = d2 - 10*q;

    d3 = q + 4*d3;
    d4 = (d3 * 0x1A) >> 8;
    d3 = d3 - 10*d4;

    digits[0] = (d4<<4) | (d3);
    digits[1] = (d2<<4) | (d1);
    digits[2] = (d0<<4);
}

ลิงค์ที่ดีขอบคุณ! ไม่เพียงเพิ่มประสิทธิภาพความเร็วเท่านั้น แต่ยังลดขนาดรหัสด้วย ฉันได้ติดตั้ง "12 บิตไบนารีถึง 4 หลัก ASCII ทศนิยม" จากลิงก์ของคุณเพราะมันไม่เกี่ยวข้องกับการคูณ
Donotalo

8

สมมติว่าเป็นจำนวนเต็มหารและการคูณสามารถเกิดขึ้นจากการเปลี่ยนแปลงบิต และจากการหารและการคูณ (จำนวนเต็ม) โมดูโลสามารถรับได้

หากต้องการคูณด้วย 10:

y = (x << 3) + (x << 1);

การหารด้วย 10 นั้นยากกว่า ฉันรู้อัลกอริทึมการหารหลายอย่าง หากฉันจำได้ถูกต้องมีวิธีหาร 10 อย่างรวดเร็วด้วยการเปลี่ยนบิตและการลบ แต่ฉันจำวิธีที่แน่นอนไม่ได้ หากที่ไม่จริงแล้วนี้เป็นขั้นตอนวิธีการหารซึ่งจัดการ <130 รอบ ฉันไม่แน่ใจว่าคุณใช้ไมโครขนาดไหน แต่คุณสามารถใช้มันในบางวิธีแม้ว่าคุณจะต้องพอร์ต

แก้ไข: ใครบางคนบอกว่าที่ Stack Overflowหากคุณสามารถทนต่อข้อผิดพลาดเล็กน้อยและมีการลงทะเบียนชั่วคราวขนาดใหญ่สิ่งนี้จะทำงาน:

temp = (ms * 205) >> 11;  // 205/2048 is nearly the same as /10

สมมติว่าคุณมีการหารและการคูณโมดูโลนั้นง่าย:

mod = x - ((x / z) * z)

6

คุณสามารถแปลงจากไบนารีไปเป็น BCD ที่ไม่มีการแบ่งใด ๆ โดยใช้อัลกอริธึมแบบตะลุยซ้ำ มันใช้เพียงเปลี่ยนและเพิ่ม 3

ตัวอย่างเช่นแปลง 243 10 = 11110011 2เป็นไบนารี่

0000 0000 0000   11110011   Initialization
0000 0000 0001   11100110   Shift
0000 0000 0011   11001100   Shift
0000 0000 0111   10011000   Shift
0000 0000 1010   10011000   Add 3 to ONES, since it was 7
0000 0001 0101   00110000   Shift
0000 0001 1000   00110000   Add 3 to ONES, since it was 5
0000 0011 0000   01100000   Shift
0000 0110 0000   11000000   Shift
0000 1001 0000   11000000   Add 3 to TENS, since it was 6
0001 0010 0001   10000000   Shift
0010 0100 0011   00000000   Shift
   2    4    3
       BCD

อัลกอริทึมนี้มีประสิทธิภาพมากเมื่อไม่มีตัวหารฮาร์ดแวร์ มีการใช้การเลื่อนซ้ายโดย 1 เพียงอย่างเดียวดังนั้นจึงรวดเร็วแม้ว่าจะไม่สามารถใช้ตัวเปลี่ยนบาร์เรลได้


4

ขึ้นอยู่กับจำนวนของตัวเลขที่คุณต้องการคุณอาจจะสามารถใช้วิธีการเดรัจฉานบังคับ ( d- หมายเลขอินพุตt- - สตริง ASCII เอาท์พุท):

t--;
if (d >= 1000) t++; *t = '0'; while (d >= 1000) { d -= 1000; *t += 1; }
if (d >= 100) t++; *t = '0'; while (d >= 100) { d -= 100; *t += 1;}
if (d >= 10) t++; *t = '0'; while (d >= 10) { d -= 10; *t += 1;}
t++; *t = '0' + d;

นอกจากนี้คุณยังสามารถเปลี่ยนหลาย ifs เป็นวงด้วยพลังของสิบที่ได้จากการคูณหรือตารางการค้นหา


2

หมายเหตุแอปพลิเคชันนี้อธิบายอัลกอริทึมสำหรับ BCD เลขคณิตรวมถึงการแปลงจากไบนารีเป็น BCD และในทางกลับกัน Appnote นั้นเป็นของ Atmel ซึ่งเป็น AVR แต่อัลกอริธึมที่อธิบายไว้นั้นไม่ขึ้นกับหน่วยประมวลผล


1

ฉันไม่มีคำตอบที่ดี แต่มีการสนทนาที่ดีกับ Stack Overflow ในเว็บไซต์น้องสาวของเราในหัวข้อเดียวกันของการหารและการเพิ่มประสิทธิภาพโมดูโล

คุณมีหน่วยความจำเพียงพอที่จะใช้ตารางการค้นหาหรือไม่?

แฮกเกอร์ดีไลท์มีบทความเกี่ยวกับอัลกอริทึมการหารที่ดีที่สุด


ไม่มีหน่วยความจำไม่เพียงพอ ฉันต้องการทำสิ่งนั้นโดยใช้การบวกการลบและการเลื่อนบิต
Donotalo

1

คุณคิดว่าการถือค่านั้นเป็น BCD ตลอดเวลา (โดยใช้รูทีนย่อย "BCD พิเศษ" และ "เพิ่ม BCD" แบบธรรมดา) แทนที่จะเก็บค่านั้นในรูปแบบไบนารีและแปลงเป็น BCD ตามต้องการ (ใช้การแปลงที่เข้าใจยากขึ้น) จากไบนารีเป็น BCD "รูทีนย่อย" หรือไม่

ในครั้งเดียวคอมพิวเตอร์ทุกเครื่องเก็บข้อมูลทั้งหมดเป็นเลขทศนิยม (เกียร์สิบตำแหน่ง, หลอดสุญญากาศรหัสสองในห้า, BCD, ฯลฯ ) และมรดกดังกล่าวยังคงมีอยู่ในปัจจุบัน (ดูทำไมชิปนาฬิกาแบบเรียลไทม์จึงใช้ BCD )


ตัวเลขที่จะแสดงบน LCD เป็นตัวแปรตั้งแต่ -1999 ถึง 1999. มันแสดงถึงอุณหภูมิและคำนวณในรูปแบบไบนารี่
Donotalo

1

PICListเป็นทรัพยากรที่น่าตื่นตาตื่นใจสำหรับคนเขียนโปรแกรมประมวลผล PIC

การแปลง BCD

คุณได้พิจารณาใช้รูทีนย่อย binary-to-BCD ที่ผ่านการทดลองและทดสอบแล้วซึ่งได้รับการปรับให้เหมาะสมสำหรับ PIC16F โดยเฉพาะหรือไม่?

โดยเฉพาะอย่างยิ่งผู้คนใน PICList ใช้เวลาส่วนใหญ่ในการปรับการแปลงแบบไบนารีเป็น BCD ใน PIC16F รูทีนเหล่านั้น (แต่ละมือที่ปรับให้เหมาะสำหรับขนาดเฉพาะ) สรุปได้ที่ "PIC Microcontoller Radix วิธีการแปลงคณิตศาสตร์" http://www.piclist.com/techref/microchip/math/radix/index.htm

การหารจำนวนเต็มและตัวดัดแปลง

บน CPU เช่น PIC16F รูทีนย่อยที่เชี่ยวชาญในการหารด้วยค่าคงที่มักจะเร็วกว่ารูทีนทั่วไป "การหารตัวแปร A โดยตัวแปร B" ตามวัตถุประสงค์ทั่วไป คุณอาจต้องการให้ค่าคงที่ของคุณ (ในกรณีนี้คือ "0.1") ใน "การสร้างรหัสสำหรับการคูณ / หารคงที่" http://www.piclist.com/techref/piclist/codegen/constdivmul.htm หรือดูที่ กิจวัตรกระป๋องใกล้http://www.piclist.com/techref/microchip/math/basic.htm


1

เมื่อพิจารณาจากฮาร์ดแวร์ 8x8 คูณหนึ่งสามารถคำนวณ divmod-10 ของจำนวนขนาดใดก็ได้โดยใช้รูทีนซึ่งคำนวณมันสำหรับตัวเลข 12 บิตในช่วง 0-2559 ผ่านโพรซีเดอร์:

  1. สมมติว่าตัวเลขดั้งเดิมเป็น OrigH: OrigL
  2. หารหมายเลขเดิมด้วยสองและเก็บไว้ใน TempH: TempL
  3. เพิ่ม MSB ของ TempL * 51 เข้ากับ LSB ของ TempH * 51 นั่นคือความฉลาดโดยประมาณ
  4. คูณความฉลาดโดยประมาณด้วย 10 ละทิ้ง MSB ของค่า
  5. ลบ LSB ของผลลัพธ์นั้นออกจาก LSB ของหมายเลขเดิม
  6. หากค่านั้นคือ 10 หรือมากกว่า (สูงสุดจะเป็น 19) ให้ลบ 10 และเพิ่ม 1 ลงในความฉลาดโดยประมาณ

ฉันขอแนะนำให้เขียนชุดคำสั่ง divmod ซึ่ง MSB ของจำนวนจะเป็น W และ LSB ชี้โดย FSR รูทีนควรเก็บผลหารใน FSR ด้วยการลดลงของโพสต์และปล่อยให้ส่วนที่เหลืออยู่ใน W เพื่อแบ่ง 32- บิตยาว 10 โดยหนึ่งแล้วจะใช้สิ่งที่ชอบ:

  movlw 0
  lfsr 0, _number + 3; ชี้ไปที่ MSB
  โทรหา _divmod10_step
  โทรหา _divmod10_step
  โทรหา _divmod10_step
  โทรหา _divmod10_step

ขั้นตอน divmod-6 จะคล้ายกันมากยกเว้นการใช้ค่าคงที่ที่ 85 และ 6 มากกว่า 51 และ 10 ไม่ว่าในกรณีใดฉันคาดหวังว่า divmod10_step จะเป็นรอบที่ 20 (บวกสี่สำหรับการโทร / ส่งคืน) ดังนั้น divmod10 สั้น ๆ ประมาณ 50 รอบและ divmod10 ที่ยาวจะอยู่ที่ประมาณ 100 (ถ้ามีกรณีพิเศษในขั้นตอนแรกคุณสามารถประหยัดได้ไม่กี่รอบ)


1

นี่อาจไม่ใช่วิธีที่เร็วที่สุด แต่เป็นวิธีที่ง่าย

 a = 65535;

    l = 0;
    m = 0;
    n = 0;
    o = 0;
    p = 0;

    while (a >= 10000)
    {   a -= 10000;
        l += 1;
    }
     while (a >= 1000)
    {   a -= 1000;
        m += 1;
    }
     while (a >= 100)
    {   a -= 100;
        n += 1;
    }
     while (a >= 10)
    {   a -= 10;
        o += 1;
    }
     while (a > 0)
    {   a -= 1;
        p += 1;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.