Cách nhanh nhất để lấy số nguyên mod 10 và số nguyên chia 10?


10

Nếu một phần cứng không hỗ trợ các hoạt động mô đun hoặc phân chia, phải mất nhiều chu kỳ CPU hơn để mô phỏng mô đun / phân chia bằng phần mềm. Có cách nào nhanh hơn để tính toán phép chia và mô đun nếu toán hạng là 10 không?

Trong dự án của tôi, tôi thường xuyên cần tính toán mô đun số nguyên 10. Đặc biệt, tôi đang làm việc trên PIC16F và cần hiển thị một số trên màn hình LCD. Có 4 chữ số để hỗ trợ, vì vậy có 4 lệnh gọi mô đun và hàm phân chia (triển khai phần mềm). Đó là, như sau:

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

Có những lĩnh vực khác sử dụng mã tương tự.


Tại sao một vài chục cuộc gọi / giây là một vấn đề? Tôi sẽ không bận tâm trừ khi dự án có đầy đủ chức năng và không có lỗi.
Nick T

Tôi nhận thấy rằng nếu tôi liên tục hiển thị một số số trong vòng lặp bận chính, phản ứng của nút sẽ trở nên chậm. Tức là, để phát hiện ra rằng một nút đã được nhấn, tôi phải nhấn nút đó lâu hơn một chút. Điều này xảy ra khi đồng hồ hệ thống đang chạy 32768 Hz.
Donotalo

Bạn đang sử dụng ngắt? Tại sao bạn sử dụng xtal 32kHz; thông thường bạn có thể có hiệu suất năng lượng thấp hơn nếu bạn hoạt động nhanh hơn và đi ngủ khi không hoạt động.
Nick T

Tôi đang sử dụng ngắt. nhưng chỉ để cập nhật hiển thị thì không đáng để chuyển sang dao động tốc độ cao. quyền lực khôn ngoan. cho dự án của tôi. nó phải được chạy đồng hồ tốc độ thấp gần 90% thời gian sống của nó.
Donotalo

2
Như lưu ý chung, cuốn sách Delight Hacker của bởi Henry S. Warren, Jr. là các nguồn cho thông minh lừa đảo bit twiddling. Tôi đã tìm kiếm các đề xuất phân chia và nó không có gì để chia cho 10, vượt trội hơn bất kỳ câu trả lời nào dưới đây.
RBerteig

Câu trả lời:


11

Đây là thuật toán nhị phân cho BCD mà tôi đã sử dụng vài năm trước dựa trên một thuật toán được tìm thấy ở đây . Tôi đã sử dụng trình điều khiển hiển thị BCD bên ngoài đến 7 seg để kết quả có thể được ghi vào các cổng thích hợp trực tiếp dưới dạng BCD đóng gói cho đầu ra.

Điều này khá nhanh nếu bạn có hệ số nhân phần cứng trong PIC, tôi đang sử dụng PIC18F97J60. Nếu bạn không có hệ số nhân phần cứng trên PIC, hãy cân nhắc sử dụng shift + add để nhân.

Điều này có một int 16 bit không dấu và trả về BCD được đóng gói với 5 chữ số, nó có thể được sửa đổi và thực hiện nhanh hơn cho 4 chữ số. Nó sử dụng shift + bổ sung để chia xấp xỉ cho 10 nhưng với phạm vi đầu vào hạn chế, nó chính xác cho việc sử dụng này. Bạn có thể muốn đóng gói kết quả khác nhau cũng như để phù hợp với cách bạn sử dụng kết quả.

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

liên kết tuyệt vời, cảm ơn! Nó không chỉ tối ưu hóa tốc độ, nó cũng làm giảm kích thước mã. Tôi đã triển khai "nhị phân 12 bit đến 4 chữ số thập phân ASCII" từ liên kết của bạn vì điều đó không liên quan đến bất kỳ phép nhân nào.
Donotalo

8

Giả sử số nguyên không dấu, phép chia và phép nhân có thể được hình thành từ sự dịch chuyển bit. Và từ phép chia và số nhân (số nguyên), modulo có thể được suy ra.

Để nhân với 10:

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

Để chia cho 10 thì khó hơn. Tôi biết một số thuật toán phân chia. Nếu tôi nhớ lại một cách chính xác, có một cách để chia cho 10 cách nhanh chóng bằng cách sử dụng dịch chuyển bit và phép trừ, nhưng tôi không thể nhớ phương pháp chính xác. Nếu điều đó không đúng, thì đây là thuật toán phân chia quản lý <130 chu kỳ . Tôi không chắc bạn đang sử dụng loại micro nào, nhưng bạn có thể sử dụng nó theo một cách nào đó, ngay cả khi bạn phải chuyển nó.

EDIT: Ai đó nói qua tại Stack Overflow , nếu bạn có thể chịu được một chút lỗi và có một thanh ghi tạm thời lớn, điều này sẽ hoạt động:

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

Giả sử bạn có phép chia và phép nhân, modulo rất đơn giản:

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

6

Bạn có thể chuyển đổi từ nhị phân sang BCD đóng gói mà không cần phân chia bằng thuật toán dabble kép . Nó chỉ sử dụng shiftthêm 3 .

Ví dụ: chuyển đổi 243 10 = 11110011 2 thành nhị phân

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

Thuật toán này rất hiệu quả khi không có ước số phần cứng khả dụng. Nhiều hơn chỉ thay đổi trái 1 bởi được sử dụng, vì vậy nó nhanh chóng ngay cả khi không có bộ chuyển động thùng


4

Tùy thuộc vào số lượng chữ số bạn cần, bạn có thể sử dụng phương pháp brute force ( d- số đầu vào, t- chuỗi ASCII đầu ra):

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;

Bạn cũng có thể thay đổi nhiều if thành một vòng lặp, với lũy thừa mười được lấy bằng cách nhân hoặc bảng tra cứu.


2

Ghi chú ứng dụng này mô tả các thuật toán cho số học BCD, bao gồm chuyển đổi từ nhị phân sang BCD và ngược lại. Các chú thích là của Atmel, là AVR, nhưng các thuật toán được mô tả là độc lập với bộ xử lý.


1

Tôi không có câu trả lời hay, nhưng có một cuộc thảo luận tuyệt vời trên trang web chị em của chúng tôi Stack Overflow về cùng một chủ đề chính xác về phân chia và tối ưu hóa modulo.

Bạn có đủ bộ nhớ để thực hiện bảng tra cứu không?

Hackers Delight có một bài viết về các thuật toán phân chia tối ưu.


không, không có đủ bộ nhớ Tôi muốn làm điều đó bằng cách sử dụng phép cộng, phép trừ và dịch chuyển bit.
Donotalo

1

Bạn có từng cân nhắc việc giữ giá trị đó là BCD mọi lúc (sử dụng các chương trình con "BCD add" đặc biệt đơn giản và "BCD add"), thay vì giữ giá trị đó ở dạng nhị phân và chuyển đổi sang BCD khi cần (sử dụng một chuyển đổi khó hiểu hơn từ nhị phân đến BCD "chương trình con)?

Tại một thời điểm, tất cả các máy tính lưu trữ tất cả dữ liệu dưới dạng chữ số thập phân (bánh răng mười vị trí, ống chân không hai mã, BCD, v.v.), và di sản đó vẫn còn tồn tại đến ngày nay. (xem Tại sao chip đồng hồ thời gian thực sử dụng BCD ).


Số được hiển thị trên màn hình LCD là một biến số, dao động từ -1999 đến 1999. Nó cho biết nhiệt độ và được tính theo định dạng nhị phân.
Donotalo

1

Các PICList là một nguồn tuyệt vời cho những người lập trình vi xử lý PIC.

Chuyển đổi BCD

Bạn đã cân nhắc sử dụng chương trình con nhị phân-BCD đã thử nghiệm và thử nghiệm được tối ưu hóa cụ thể cho PIC16F chưa?

Cụ thể, mọi người trên PICList đã dành nhiều thời gian để tối ưu hóa chuyển đổi nhị phân sang BCD trên PIC16F. Các thói quen đó (mỗi một được tối ưu hóa cho một kích thước cụ thể) được tóm tắt tại "Phương pháp toán học chuyển đổi cơ chế PIC Microcontoller" http://www.piclist.com/techref/microchip/math/radix/index.htm

chia số nguyên và mod

Trên CPU như PIC16F, một chương trình con chuyên chia cho hằng số thường nhanh hơn nhiều so với thói quen "chia biến A cho biến B" cho mục đích chung. Bạn có thể muốn đặt hằng số của mình (trong trường hợp này là "0,1") trong "Tạo mã cho phép nhân / chia liên tục" http://www.piclist.com/techref/piclist/codegen/constdivmul.htmlm hoặc kiểm tra thói quen đóng hộp gần http://www.piclist.com/techref/microchip/math/basic.htm .


1

Với bội số phần cứng 8 x 8, người ta có thể tính divmod-10 của một số có kích thước tùy ý bằng cách sử dụng một thói quen tính toán nó cho số 12 bit trong phạm vi 0-2559 thông qua thủ tục:

  1. Giả sử số gốc trong OrigH: OrigL
  2. Chia số gốc cho hai và lưu số đó trong TempH: TempL
  3. Thêm MSB của TempL * 51 vào LSB của TempH * 51. Đó là thương số gần đúng
  4. Nhân thương số gần đúng với 10, loại bỏ MSB của giá trị.
  5. Trừ LSB của kết quả đó từ LSB của số ban đầu.
  6. Nếu giá trị đó là 10 hoặc lớn hơn (tối đa sẽ là 19), hãy trừ 10 và thêm 1 vào thương số gần đúng

Tôi sẽ đề nghị viết một thói quen divmod mà MSB của số sẽ ở W và LSB được chỉ ra bởi FSR; thói quen nên lưu trữ thương số trong FSR với phần sau giảm dần và để phần còn lại trong W. Để chia 32 bit dài cho 10, người ta sẽ sử dụng một cái gì đó như:

  di chuyển 0
  lfsr 0, _number + 3; Chỉ vào MSB
  gọi _divmod10_step
  gọi _divmod10_step
  gọi _divmod10_step
  gọi _divmod10_step

Một bước divmod-6 sẽ rất giống nhau, ngoại trừ sử dụng các hằng số 85 và 6 thay vì 51 và 10. Trong cả hai trường hợp, tôi sẽ mong đợi divmod10_step sẽ là 20 chu kỳ (cộng với bốn cho cuộc gọi / trả lại), vì vậy một divmod10 ngắn sẽ được khoảng 50 chu kỳ và một divmod10 dài sẽ là khoảng 100 (nếu một trường hợp đặc biệt là bước đầu tiên, người ta có thể lưu một vài chu kỳ).


1

Đây có thể không phải là nhanh nhất nhưng là một cách đơn giản.

 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;
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.