วิธีที่มีประสิทธิภาพในการกำหนดจำนวนหลักในจำนวนเต็ม


144

วิธีที่มีประสิทธิภาพมากในการกำหนดจำนวนหลักที่มีในจำนวนเต็มใน C ++ คืออะไร?


11
ในฐานอะไร 2? 10?
จาค็อบครัลล์

2
ฉันต้องการจะทำในฐาน 10
เซท

1
ฉันเคยถามคำถามที่เกี่ยวข้อง: คุณจะได้รับตัวเลขแรกใน int ได้อย่างไร มีการใช้วิธีการเดียวกันหลายวิธีในการตอบของผู้คน นี่คือลิงค์ในกรณีที่เกี่ยวข้องกับงานของคุณ [ stackoverflow.com/questions/701322/]
Dinah

การประกอบแบบอินไลน์มีคุณสมบัติหรือไม่
György Andrasek

1
ในขณะที่คำตอบทั้งหมดเหล่านี้อยู่ในรูปของฐาน 10 มันค่อนข้างง่ายที่จะเปลี่ยนเพื่อคำนวณผลลัพธ์สำหรับฐานที่ต้องการ
Ira Baxter

คำตอบ:


106

วิธีที่มีประสิทธิภาพที่สุดสมมติว่าคุณรู้ขนาดของจำนวนเต็มจะเป็นการค้นหา ควรเร็วกว่าวิธีลอการิทึมที่สั้นกว่ามาก หากคุณไม่สนใจเกี่ยวกับการนับ '-' ให้ลบ +1

// generic solution
template <class T>
int numDigits(T number)
{
    int digits = 0;
    if (number < 0) digits = 1; // remove this line if '-' counts as a digit
    while (number) {
        number /= 10;
        digits++;
    }
    return digits;
}

// partial specialization optimization for 32-bit numbers
template<>
int numDigits(int32_t x)
{
    if (x == MIN_INT) return 10 + 1;
    if (x < 0) return numDigits(-x) + 1;

    if (x >= 10000) {
        if (x >= 10000000) {
            if (x >= 100000000) {
                if (x >= 1000000000)
                    return 10;
                return 9;
            }
            return 8;
        }
        if (x >= 100000) {
            if (x >= 1000000)
                return 7;
            return 6;
        }
        return 5;
    }
    if (x >= 100) {
        if (x >= 1000)
            return 4;
        return 3;
    }
    if (x >= 10)
        return 2;
    return 1;
}

// partial-specialization optimization for 8-bit numbers
template <>
int numDigits(char n)
{
    // if you have the time, replace this with a static initialization to avoid
    // the initial overhead & unnecessary branch
    static char x[256] = {0};
    if (x[0] == 0) {
        for (char c = 1; c != 0; c++)
            x[c] = numDigits((int32_t)c);
        x[0] = 1;
    }
    return x[n];
}

5
อาจเร็วกว่าคำตอบของฉันทำได้ดีมาก เพื่อประสิทธิภาพที่เพิ่มขึ้นถ้าคุณรู้ว่าตัวเลขอินพุตของคุณส่วนใหญ่จะเล็ก (ฉันเดาน้อยกว่า 100,000) ดังนั้นให้ทำการทดสอบกลับ: ถ้า (x <10) ส่งคืน 1; ถ้า (x <100) ส่งคืน 2; เป็นต้นเพื่อให้ฟังก์ชั่นทำการทดสอบน้อยลงและออกเร็วขึ้น
squelart

29
หรืออาจเรียงลำดับใหม่และซ้อนคำสั่ง if เพื่อทำการค้นหาแบบไบนารีแทนการค้นหาแบบเชิงเส้น
dave4420

1
นั่นไม่ใช่ความคิดที่ดี จะเกิดอะไรขึ้นเมื่อสถาปัตยกรรมขยายเป็นจำนวนเต็ม 256 บิต คุณต้องอย่าลืมกลับมาและแก้ไขรหัสนี้ ในชีวิตจริงที่จะไม่เกิดขึ้นและสิ่งนี้อาจจะถูกใช้เพื่อสร้างบัฟเฟอร์ขนาดที่ถูกต้องที่คุณกำลังเปิดตัวเองทุกประเภทของบัฟเฟอร์มากกว่าปัญหาการทำงานในสถาปนิกขนาดใหญ่
มาร์ตินนิวยอร์ก

3
สมมติว่ามีการแจกแจงแบบตัวเลขการค้นหาแบบย้อนกลับแบบย้อนกลับ (เริ่มจากตัวเลขสูงสุดถึง 1) อาจเร็วกว่าการค้นหาแบบไบนารีโดยเฉลี่ยเนื่องจากมีตัวเลขค่อนข้างมากด้วย N ตัวเลขมากกว่า N-1 หลัก graphics.stanford.edu/~ seander / ...
ฟะ

6
ฉันไม่ต้องกังวลอย่างหนักเกี่ยวกับจำนวนเต็ม 256 หรือ 128 บิต นอกจากว่าคุณจะต้องนับจำนวนอิเล็กตรอนในจักรวาล (ครั้งที่ 10 ครั้งที่ 78 ที่ฉันทำไป) 64 บิตจะทำได้ค่อนข้างดี เครื่อง 32 บิตกินเวลา ~~ 15 ปี ฉันเดาว่าเครื่อง 64 บิตจะใช้งานได้นานขึ้น สำหรับจำนวนที่มากขึ้นการคำนวณทางคณิตศาสตร์แบบหลายค่าจะถูกต้องและฉันสงสัยว่าประสิทธิภาพของการคำนวณจำนวนหลักจะมีความสำคัญหรือไม่
Ira Baxter

74

วิธีที่ง่ายที่สุดคือทำ:

unsigned GetNumberOfDigits (unsigned i)
{
    return i > 0 ? (int) log10 ((double) i) + 1 : 1;
}

log10 ถูกกำหนดไว้ในหรือ<cmath> <math.h>คุณจะต้องทำโพรไฟล์นี้เพื่อดูว่ามันเร็วกว่าโพสต์อื่น ๆ ที่นี่หรือไม่ ฉันไม่แน่ใจว่ามันแข็งแกร่งแค่ไหนในเรื่องของความแม่นยำของจุดลอย นอกจากนี้อาร์กิวเมนต์ไม่ได้ลงนามเป็นค่าลบและบันทึกไม่ได้ผสมกันจริงๆ


7
สำหรับ 32 บิต int และ 56 บิตลอยอาจเป็นไปได้ หากอินพุตยาว (64 บิต) บันทึก 56 บิตของความแม่นยำสองเท่าอาจเป็นสาเหตุทำให้การตอบถูกผิดในกรณีที่ค่าใกล้เคียงกับค่ามากที่ 10 ^ n คาดหวังปัญหาข้างต้น 2 ^ 50
Ira Baxter

1
นอกจากนี้ยังมีคำถามว่าฟังก์ชั่นบันทึกมีความแม่นยำเพียงใด ฉันไม่ได้ตรวจสอบความถูกต้องว่าพวกเขาอยู่ในห้องสมุดสมัยใหม่และไม่สบายใจที่จะเชื่อใจพวกเขาว่าเป็นส่วนหนึ่งในพันล้าน
David Thornley

@DavidThornley: บันทึกหรือฟังก์ชั่นคณิตศาสตร์อื่น ๆ มีความแม่นยำอย่างสมบูรณ์แบบเว้นแต่จะระบุไว้ในบรรทัดคำสั่งคอมไพเลอร์ บางส่วนจะถูกแปลงเป็น x86 ภายในเวลารวบรวม บางคนไม่มีอยู่และจะขยายไปสู่สูตรของอินทรินที่มีอยู่ ตัวอย่างเช่นหากใช้-fpfastคุณจะเห็นการใช้งาน SSE instrinsics มากกว่า x87 ซึ่งให้การรับประกันความแม่นยำ IIRC ที่น้อยกว่า แต่โดยค่าเริ่มต้นไม่มีปัญหา
v.oddou

@ DavidThornley: มันเป็นมากกว่าความแม่นยำ คำถามคือจะรับประกันหรือไม่ว่า log10 (10 ^ k) ≥ k สำหรับ k ที่เกี่ยวข้องทั้งหมด นั่นคือมันรับประกันได้ว่าข้อผิดพลาดในการปัดเศษที่หลีกเลี่ยงไม่ได้จะไปในทิศทางที่ถูกต้อง k + eps เป็นผลใช้งาน k - eps ไม่ได้ และ "แม่นยำอย่างสมบูรณ์แบบ" ไร้เดียงสา
gnasher729

1
การทดสอบ i> 0 สามารถปรับให้เหมาะกับ i> 9
Pat

60

บางทีฉันอาจเข้าใจผิดคำถาม แต่มันไม่ได้ใช่ไหม

int NumDigits(int x)  
{  
    x = abs(x);  
    return (x < 10 ? 1 :   
        (x < 100 ? 2 :   
        (x < 1000 ? 3 :   
        (x < 10000 ? 4 :   
        (x < 100000 ? 5 :   
        (x < 1000000 ? 6 :   
        (x < 10000000 ? 7 :  
        (x < 100000000 ? 8 :  
        (x < 1000000000 ? 9 :  
        10)))))))));  
}  

29
และฉันจะไม่แปลกใจถ้าวิธีนี้จะเร็วที่สุด
VisioN

32
int digits = 0; while (number != 0) { number /= 10; digits++; }

หมายเหตุ: "0" จะมีตัวเลข 0 หลัก! หากคุณต้องการให้ 0 ปรากฏว่ามี 1 หลักให้ใช้:

int digits = 0; do { number /= 10; digits++; } while (number != 0);

(ขอบคุณ Kevin Fegan)

ในที่สุดใช้ profiler เพื่อทราบว่าคำตอบทั้งหมดของที่นี่จะเร็วกว่าบนเครื่องของคุณ ...


3
สิ่งนี้อาจจะเร็วหรือเร็วกว่าวิธีวนลูปที่ยังไม่ได้เปิดซึ่งคุณต้องทำการแยกความแตกต่าง (ควรจะเล็กน้อยในระยะยาว)
Vitali

การตกลงการทำโปรไฟล์เป็นวิธีเดียวที่จะรู้ได้อย่างแน่นอน! ฉันอัปเดตคำตอบของฉันด้วยความคิดเห็นนั้นเนื่องจากคำตอบ ceil (log10 ()) ของหายไป
squelart

11

เล่นตลก: นี่คือวิธีที่มีประสิทธิภาพมากที่สุด (จำนวนของตัวเลขที่มีการคำนวณที่รวบรวมเวลา):

template <unsigned long long N, size_t base=10>
struct numberlength
{
    enum { value = 1 + numberlength<N/base, base>::value };
};

template <size_t base>
struct numberlength<0, base>
{
    enum { value = 0 };
};

อาจมีประโยชน์ในการกำหนดความกว้างที่จำเป็นสำหรับฟิลด์ตัวเลขในการจัดรูปแบบองค์ประกอบอินพุต ฯลฯ


4
ก่อนอื่นโซลูชันของคุณใช้ไม่ได้กับ 0 ประการที่สองโซลูชันของคุณไม่สามารถใช้กับกรณีทั่วไปของตัวแปรได้ ประการที่สามถ้าคุณใช้ตัวอักษรคงที่คุณก็รู้แล้วว่ามีกี่หลัก
Vitali

มันใช้งานได้สำหรับ 0 ด้วย นอกจากนี้ยังใช้งานได้กับฐานใด ๆ ส่วนที่เหลือเป็นคะแนนที่ถูกต้องที่ฉันระบุไว้แล้ว
blinnov.com

3
ฉันไม่คิดว่ามันจะจริง มันล้มเหลวบน0และยังล้มเหลวในฐาน1:) 0และให้หารด้วยข้อผิดพลาดของศูนย์ถ้าฐานจะได้รับเป็น มันสามารถแก้ไขได้ อย่างไรก็ตามฉันวางท่าโพสต์เก่ามากขออภัยด้วยที่ฉันคิดว่านี่ไม่จำเป็นต้องเป็นเรื่องตลกและอาจมีประโยชน์จริง ๆ
tjm

9

ดูBit Twiddling Hacksสำหรับคำตอบที่สั้นกว่าที่คุณยอมรับ นอกจากนี้ยังมีประโยชน์ในการค้นหาคำตอบได้เร็วขึ้นหากการกระจายข้อมูลของคุณตามปกติโดยการตรวจสอบค่าคงที่ขนาดใหญ่ก่อน (v >= 1000000000)จับ 76% ของค่าดังนั้นการตรวจสอบว่าโดยเฉลี่ยก่อนจะเร็วกว่า


มันไม่ชัดเจนว่าการบิดสองบิตเร็วกว่าจริงๆ แม้ในกรณีที่เลวร้ายที่สุดวิธีการแก้ไขของฉันต้องมีการเปรียบเทียบ 4 รายการ (อาจสามารถลดได้ถึง 3 หากฉันตรวจสอบการแบ่งพาร์ติชันเพิ่มเติม ฉันสงสัยอย่างจริงจังว่าสิ่งนี้จะถูกเอาชนะโดยการดำเนินการทางคณิตศาสตร์ + การโหลดหน่วยความจำ (แม้ว่าจะเข้าถึงได้เพียงพอ แต่สิ่งเหล่านั้นจะหายไปในแคชของ CPU) โปรดจำไว้ว่าในตัวอย่างที่พวกเขาให้พวกเขายังซ่อน log log 2 เป็นฟังก์ชั่น abstract บางส่วนของ IntegerLogBase2 (ซึ่งจริงๆแล้วมันไม่ถูกเลย)
Vitali

เช่นเดียวกับการติดตามใช่ถ้าหมายเลขถูกแจกจ่ายตามปกติการตรวจสอบตามลำดับจะเร็วขึ้น อย่างไรก็ตามมันมีกรณีที่เลวลงว่าจะช้าเป็นสองเท่าในกรณีที่เลวร้ายที่สุด วิธีการแบ่งพาร์ติชันตามจำนวนหลักแทนพื้นที่อินพุตหมายความว่าพฤติกรรมไม่ได้มีตัวพิมพ์เล็กและดำเนินการอย่างเหมาะสมเสมอ นอกจากนี้อย่าลืมว่าคุณกำลังตั้งสมมติฐานว่าตัวเลขจะถูกกระจายอย่างสม่ำเสมอ ในความเป็นจริงพวกเขามีแนวโน้มที่จะปฏิบัติตามการกระจายที่เกี่ยวข้องกับ <a href=" en.wikipedia.org/wiki/... > จะคาดเดาของฉัน.
Vitali

การแฮ็กนิด ๆ หน่อย ๆ นั้นไม่เร็วกว่าวิธีการทำพาร์ติชั่นด้านบน แต่มันอาจจะน่าสนใจถ้าคุณมีเคสทั่วไปมากกว่าแบบลอยตัวที่นี่
Corwin Joy

1
การแฮ็กแบบทวิปปิ้งบิตแนะนำวิธีรับ int log10 เนื่องจาก int log2 มันแสดงให้เห็นหลายวิธีที่จะได้รับ int log2 ส่วนใหญ่เกี่ยวข้องกับการเปรียบเทียบ / สาขาน้อย (ฉันคิดว่าคุณประเมินค่าใช้จ่ายของสาขาที่ไม่สามารถคาดเดาได้ Vitali) หากคุณสามารถใช้ inline x86 asm คำสั่ง BSR จะให้ค่า int log2 ของค่า (เช่นดัชนีบิตของบิตเซ็ตที่สำคัญที่สุด) มันช้าเล็กน้อยใน K8 (10 รอบแฝง) แต่รวดเร็วใน Core 2 (2 หรือ 3 รอบแฝง) แม้แต่ K8 ก็อาจเร็วกว่าการเปรียบเทียบ
ปีเตอร์ Cordes

บน K10 lzcnt จะนับศูนย์นำหน้าดังนั้นมันเกือบจะเหมือนกับ bsr แต่อินพุต 0 ไม่ใช่กรณีพิเศษอีกต่อไปที่มีผลลัพธ์ที่ไม่ได้กำหนด Latencies: BSR: 4, LZCNT: 2
Peter Cordes

8

แปลงเป็นสตริงแล้วใช้ฟังก์ชันในตัว

unsigned int i;
cout<< to_string(i).length()<<endl;

7
int x = 1000;
int numberOfDigits = x ? static_cast<int>(log10(abs(x))) + 1 : 1;

3
แม้ว่าสิ่งนี้จะมีประสิทธิภาพในแง่ของ LOCs ดังที่ระบุไว้ในบันทึกการใช้คำตอบที่ยอมรับแล้วอาจไม่ให้ประสิทธิภาพที่ดีที่สุด
Ian

@Ian ทำไมไม่ เป็นเพียงคำแนะนำของ FPU สองสามข้อ ไมล์ดีกว่าทุกกิ่งและลูปในคำตอบอื่น ๆ
มาร์ควิสแห่ง Lorne

5

โปสเตอร์ก่อนหน้านี้แนะนำลูปที่หารด้วย 10 เนื่องจากการทวีคูณบนเครื่องจักรที่ทันสมัยนั้นเร็วกว่ามากฉันจึงแนะนำรหัสต่อไปนี้แทน:

 int digits = 1, pten=10; while ( pten <= number ) { digits++; pten*=10; }

1
มารอยู่ในรายละเอียด - สิ่งที่เกิดขึ้นกับการพูดมาตรฐาน :: numeric_limits <int> :: จำนวน == สูงสุด - มันอาจจะมีปัญหาในการยุติ
pgast

2
หากคุณกังวลเกี่ยวกับกรณีดังกล่าวคุณสามารถเพิ่ม IF เพิ่มเติมหนึ่งรายการเพื่อจัดการค่าที่มีขนาดใหญ่มาก
Ira Baxter

2
ฉันควรสังเกตว่าในเครื่อง x86 คูณด้วยค่าคงที่ 10 ตามที่ใช้ในกรณีนี้สามารถนำไปใช้จริงโดยคอมไพเลอร์เช่น LEA R2, [8 * R1 + R1], เพิ่ม R1, R2 ดังนั้นมันจึงใช้เวลา 2 นาฬิกาสูงสุด การคูณด้วยตัวแปรใช้เวลาหลายสิบนาฬิกาและการแบ่งจะแย่กว่านั้นมาก
Ira Baxter

ข้อดีของวิธีหารคือคุณไม่ต้องกังวลเกี่ยวกับจำนวนลบ
โยฮันเนส Schaub - litb

1
ฉันเปรียบเทียบวิธีการคูณ (ด้วย fabs เพื่อลบปัญหาเครื่องหมาย) กับแนวทางการหาร บนเครื่องของฉันวิธีการแบ่งเป็นปัจจัยที่ 2 ช้ากว่าวิธีการคูณ ไม่ว่าจะเป็นการเพิ่มประสิทธิภาพก่อนวัยอันควรหรือไม่ขึ้นอยู่กับสถานที่และวิธีการที่เรียกว่า
Spacemoose

5

สถาปัตยกรรม ppc มีคำสั่งการนับเล็กน้อย ด้วยสิ่งนี้คุณสามารถกำหนด log ฐาน 2 ของจำนวนเต็มบวกในคำสั่งเดียว ตัวอย่างเช่น 32 บิตจะเป็น:

#define log_2_32_ppc(x) (31-__cntlzw(x))

หากคุณสามารถจัดการกับข้อผิดพลาดเล็กน้อยในค่าขนาดใหญ่คุณสามารถแปลงเป็นล็อกฐาน 10 ด้วยคำแนะนำอื่น ๆ :

#define log_10_estimate_32_ppc(x) (9-(((__cntlzw(x)*1233)+1545)>>12))

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

ฉันรู้เพียงแค่คำสั่ง ppc มือ แต่สถาปัตยกรรมอื่น ๆ ควรมีคำแนะนำที่คล้ายกัน


โซลูชันนี้จะคำนวณ log2 (15) = 4 บิตและ log2 (9) = 4 บิต แต่ 15 และ 9 ต้องการตัวเลขทศนิยมที่แตกต่างกันในการพิมพ์ ดังนั้นมันจึงไม่ทำงานเว้นแต่คุณจะไม่สนใจตัวเลขที่พิมพ์ด้วยตัวเลขจำนวนมากเกินไปในบางครั้ง แต่ในกรณีนี้คุณสามารถเลือก "10" เป็นคำตอบสำหรับ int
Ira Baxter

ว้าวฟังก์ชั่นโดยประมาณ ดี
doug65536

4
 #include <iostream>
 #include <math.h>

 using namespace std;

 int main()
 {
     double num;
     int result;
     cout<<"Enter a number to find the number of digits,  not including decimal places: ";
     cin>>num;
     result = ((num<=1)? 1 : log10(num)+1);
     cout<<"Number of digits "<<result<<endl;
     return 0;
 }

นี่อาจเป็นวิธีที่ง่ายที่สุดในการแก้ปัญหาของคุณโดยสมมติว่าคุณสนใจเฉพาะตัวเลขก่อนทศนิยมและสมมติว่าอะไรก็ตามที่น้อยกว่า 10 เป็นเพียง 1 หลัก


1

ฉันชอบคำตอบของ Ira Baxter นี่คือตัวแปรเท็มเพลตที่จัดการกับขนาดและข้อตกลงต่าง ๆ ด้วยค่าจำนวนเต็มสูงสุด (อัปเดตเพื่อยกการตรวจสอบขอบเขตบน)

#include <boost/integer_traits.hpp>

template<typename T> T max_decimal()
{
    T t = 1;

    for (unsigned i = boost::integer_traits<T>::digits10; i; --i)
        t *= 10;

    return t;
}

template<typename T>
unsigned digits(T v)
{
    if (v < 0) v = -v;

    if (max_decimal<T>() <= v)
        return boost::integer_traits<T>::digits10 + 1;

    unsigned digits = 1;
    T boundary = 10;

    while (boundary <= v) {
        boundary *= 10;
        ++digits;
    }

    return digits;
}

หากต้องการรับประสิทธิภาพที่ดีขึ้นจากการยกการทดสอบเพิ่มเติมจากลูปคุณต้องมีความเชี่ยวชาญ max_decimal () เพื่อส่งคืนค่าคงที่สำหรับแต่ละประเภทบนแพลตฟอร์มของคุณ คอมไพเลอร์เวทย์มนตร์ที่เพียงพอสามารถเพิ่มประสิทธิภาพการเรียกไปยัง max_decimal () ให้คงที่ แต่ความเชี่ยวชาญจะดีกว่ากับคอมไพเลอร์ส่วนใหญ่ในปัจจุบัน เวอร์ชั่นนี้อาจช้ากว่าเนื่องจาก max_decimal มีค่าใช้จ่ายสูงกว่าการทดสอบที่ลบออกจากลูป

ฉันจะทิ้งทุกอย่างไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน


คุณต้องการให้ขีด จำกัด สูงสุดตรวจสอบการแยกตามเงื่อนไขที่ทดสอบก่อนดังนั้นคุณจึงไม่ตรวจสอบในการวนซ้ำแต่ละรอบ
Ira Baxter

คุณไม่ต้องการใส่ 10 ลงในอุณหภูมินั้น คอมไพเลอร์อาจพิจารณาการคูณด้วย t เพื่อคูณด้วยตัวแปรจริงและใช้คำสั่งคูณวัตถุประสงค์ทั่วไป ถ้าคุณเขียนว่า "result * = 10;" คอมไพเลอร์จะสังเกตเห็นการคูณด้วยค่าคงที่ 10 และใช้มันด้วยการเลื่อนและการเพิ่มเล็กน้อยซึ่งเร็วมาก
Ira Baxter

หากการคูณด้วย t เป็นการคูณด้วย 10 เสมอดังนั้นคอมไพเลอร์สามารถลดความแรงได้ อย่างไรก็ตาม t ไม่ได้เป็นแบบวนรอบในกรณีนี้ (เป็นเพียงการดัดแปลงของฟังก์ชันพลังงานจำนวนเต็มที่ฉันโกหก) การเพิ่มประสิทธิภาพที่ถูกต้องเป็นความเชี่ยวชาญในประเภทที่ส่งกลับค่าคงที่ อย่างไรก็ตามคุณคิดถูกว่าในกรณีนี้ฟังก์ชั่นจะเพิ่มกำลัง 10 เป็นพลังงานไม่ใช่จำนวนเต็มตามกำลังและการลดความแข็งแรงทำให้ได้ชัยชนะที่ดี ดังนั้นฉันจึงทำการเปลี่ยนแปลง ... เวลานี้การเปลี่ยนแปลงเพิ่มเติมจะถูกทิ้งไว้เป็นแบบฝึกหัด! (กองมากเกินเป็นอ่างครั้งใหญ่ ... )
janm

1
#include <stdint.h> // uint32_t [available since C99]

/// Determine the number of digits for a 32 bit integer.
/// - Uses at most 4 comparisons.
/// - (cX) 2014 adolfo.dimare@gmail.com
/// - \see http://stackoverflow.com/questions/1489830/#27669966
/**  #d == Number length vs Number of comparisons == #c
     \code
         #d | #c   #d | #c
         ---+---   ---+---
         10 | 4     5 | 4
          9 | 4     4 | 4
          8 | 3     3 | 3
          7 | 3     2 | 3
          6 | 3     1 | 3
     \endcode
*/
unsigned NumDigits32bs(uint32_t x) {
    return // Num-># Digits->[0-9] 32->bits bs->Binary Search
    ( x >= 100000u // [6-10] [1-5]
    ?   // [6-10]
        ( x >= 10000000u // [8-10] [6-7]
        ?   // [8-10]
            ( x >= 100000000u // [9-10] [8]
            ? // [9-10]
                ( x >=  1000000000u // [10] [9]
                ?   10
                :    9
                )
            : 8
            )
        :   // [6-7]
            ( x >=  1000000u // [7] [6]
            ?   7
            :   6
            )
        )
    :   // [1-5]
        ( x >= 100u // [3-5] [1-2]
        ?   // [3-5]
            ( x >= 1000u // [4-5] [3]
            ? // [4-5]
                ( x >=  10000u // [5] [4]
                ?   5
                :   4
                )
            : 3
            )
        :   // [1-2]
            ( x >=  10u // [2] [1]
            ?   2
            :   1
            )
        )
    );
}

0

ยังเป็นข้อมูลโค้ดอีกอันโดยทั่วไปจะทำเหมือนกับ Vitali แต่ใช้การค้นหาแบบไบนารี่ อาเรย์กำลังเริ่มต้นขี้เกียจหนึ่งครั้งต่อหนึ่งอินสแตนซ์ของประเภทที่ไม่ได้ลงชื่อ โอเวอร์โหลดประเภทที่เซ็นชื่อจะดูแลเครื่องหมายลบ

#include <limits>
#include <type_traits>
#include <array>

template <class T> 
size_t NumberOfDecPositions ( T v, typename std::enable_if<std::is_unsigned<T>::value>::type* = 0 )
{
    typedef std::array<T,std::numeric_limits<T>::digits10+1> array_type;
    static array_type powers_of_10;
    if ( powers_of_10.front() == 0 )
    {
        T n = 1;
        for ( T& i: powers_of_10 )
        {
            i = n;
            n *= 10;
        }
    }

    size_t l = 0, r = powers_of_10.size(), p;
    while ( l+1 < r )
    {
        p = (l+r)/2;
        if ( powers_of_10[p] <= v )
            l = p;
        else
            r = p;
    }
    return l + 1;
};

template <class T> 
size_t NumberOfDecPositions ( T v, typename std::enable_if<std::is_signed<T>::value>::type* = 0 )
{
    typedef typename std::make_unsigned<T>::type unsigned_type;
    if ( v < 0 )
        return NumberOfDecPositions ( static_cast<unsigned_type>(-v) ) + 1;
    else
        return NumberOfDecPositions ( static_cast<unsigned_type>(v) );
}

หากใครสนใจที่จะเพิ่มประสิทธิภาพเพิ่มเติมโปรดทราบว่าองค์ประกอบแรกของอาร์เรย์พลังไม่เคยถูกใช้และlจะปรากฏขึ้นพร้อม+12 ครั้ง


0

ในกรณีที่จำเป็นต้องใช้จำนวนของตัวเลขและค่าของแต่ละตำแหน่งหลัก:

int64_t = number, digitValue, digits = 0;    // or "int" for 32bit

while (number != 0) {
    digitValue = number % 10;
    digits ++;
    number /= 10;
}

digitให้คุณค่ากับการโพสต์ตัวเลขที่ประมวลผลในขณะนี้ในลูป ตัวอย่างเช่นสำหรับหมายเลข 1776 ค่าหลักคือ:
6 ในวงที่ 1
7 ในวงที่ 2 ที่
7 ในวงที่ 3 ที่
1 ในวงที่ 4


0
// Meta-program to calculate number of digits in (unsigned) 'N'.    
template <unsigned long long N, unsigned base=10>
struct numberlength
{   // http://stackoverflow.com/questions/1489830/
    enum { value = ( 1<=N && N<base ? 1 : 1+numberlength<N/base, base>::value ) };
};

template <unsigned base>
struct numberlength<0, base>
{
    enum { value = 1 };
};

{
    assert( (1 == numberlength<0,10>::value) );
}
assert( (1 == numberlength<1,10>::value) );
assert( (1 == numberlength<5,10>::value) );
assert( (1 == numberlength<9,10>::value) );

assert( (4 == numberlength<1000,10>::value) );
assert( (4 == numberlength<5000,10>::value) );
assert( (4 == numberlength<9999,10>::value) );

การแก้ไขสำหรับ "มุขตลก" จาก 'blinnov.com' ด้านบน
Adolfo

0
/// Determine the number of digits for a 64 bit integer.
/// - Uses at most 5 comparisons.
/// - (cX) 2014 adolfo.dimare@gmail.com
/// - \see http://stackoverflow.com/questions/1489830/#27670035
/**  #d == Number length vs Number of comparisons == #c
     \code
         #d | #c   #d | #c     #d | #c   #d | #c
         ---+---   ---+---     ---+---   ---+---
         20 | 5    15 | 5      10 | 5     5 | 5
         19 | 5    14 | 5       9 | 5     4 | 5
         18 | 4    13 | 4       8 | 4     3 | 4
         17 | 4    12 | 4       7 | 4     2 | 4
         16 | 4    11 | 4       6 | 4     1 | 4
     \endcode
*/
unsigned NumDigits64bs(uint64_t x) {
    return // Num-># Digits->[0-9] 64->bits bs->Binary Search
    ( x >= 10000000000ul // [11-20] [1-10]
    ?
        ( x >= 1000000000000000ul // [16-20] [11-15]
        ?   // [16-20]
            ( x >= 100000000000000000ul // [18-20] [16-17]
            ?   // [18-20]
                ( x >= 1000000000000000000ul // [19-20] [18]
                ? // [19-20]
                    ( x >=  10000000000000000000ul // [20] [19]
                    ?   20
                    :   19
                    )
                : 18
                )
            :   // [16-17]
                ( x >=  10000000000000000ul // [17] [16]
                ?   17
                :   16
                )
            )
        :   // [11-15]
            ( x >= 1000000000000ul // [13-15] [11-12]
            ?   // [13-15]
                ( x >= 10000000000000ul // [14-15] [13]
                ? // [14-15]
                    ( x >=  100000000000000ul // [15] [14]
                    ?   15
                    :   14
                    )
                : 13
                )
            :   // [11-12]
                ( x >=  100000000000ul // [12] [11]
                ?   12
                :   11
                )
            )
        )
    :   // [1-10]
        ( x >= 100000ul // [6-10] [1-5]
        ?   // [6-10]
            ( x >= 10000000ul // [8-10] [6-7]
            ?   // [8-10]
                ( x >= 100000000ul // [9-10] [8]
                ? // [9-10]
                    ( x >=  1000000000ul // [10] [9]
                    ?   10
                    :    9
                    )
                : 8
                )
            :   // [6-7]
                ( x >=  1000000ul // [7] [6]
                ?   7
                :   6
                )
            )
        :   // [1-5]
            ( x >= 100ul // [3-5] [1-2]
            ?   // [3-5]
                ( x >= 1000ul // [4-5] [3]
                ? // [4-5]
                    ( x >=  10000ul // [5] [4]
                    ?   5
                    :   4
                    )
                : 3
                )
            :   // [1-2]
                ( x >=  10ul // [2] [1]
                ?   2
                :   1
                )
            )
        )
    );
}

0

สำหรับจำนวนเต็ม 'X' คุณต้องการทราบจำนวนหลัก, ไม่เป็นไรโดยไม่ต้องใช้ลูปใด ๆ , วิธีนี้ทำหน้าที่ในสูตรเดียวในหนึ่งบรรทัดเท่านั้นดังนั้นนี่เป็นทางออกที่ดีที่สุดที่ฉันเคยเห็นกับปัญหานี้

 int x = 1000 ; 
 cout<<numberOfDigits = 1+floor(log10(x))<<endl ; 

ล้มเหลวสำหรับ INT_MAX และสำหรับจำนวนลบ
ranu

@ranu ล้มเหลวสำหรับ INT_MAX อย่างไร เมื่ออาร์กิวเมนต์ถูกแปลงเป็นdouble? หรือคุณกำลังอ้างอิงถึงการป้อนจำนวนเต็มเป็นไปไม่ได้ที่มีตัวเลขทศนิยม INT_MAX? ข้อใดจะทำให้คำตอบอื่น ๆ ที่นี่ล้มเหลวเช่นกัน
มาร์ควิสแห่ง Lorne

0
int numberOfDigits(int n){

    if(n<=9){
        return 1;
    }
    return 1 + numberOfDigits(n/10);
}

นี่คือสิ่งที่ฉันจะทำถ้าคุณต้องการสำหรับฐาน 10. มันค่อนข้างเร็วและคุณจะไม่ได้สแต็ค overflock ซื้อจำนวนเต็มนับ


0
int num,dig_quant = 0;
cout<<"\n\n\t\t--Count the digits in Number--\n\n";
cout<<"Enter Number: ";
cin>>num;
for(int i = 1; i<=num; i*=10){
    if(num / i  > 0){
      dig_quant += 1;
    }
}
 cout<<"\n"<<number<<" include "<<dig_quant<<" digit"
 cout<<"\n\nGoodbye...\n\n";

0

หากเร็วขึ้นมีประสิทธิภาพมากขึ้นนี่คือการปรับปรุงในการปรับปรุงandrei alexandrescuการปรับปรุงของเวอร์ชันของเขาเร็วกว่าวิธีที่ไร้เดียงสา (หารด้วย 10 ทุกหลัก) เวอร์ชันด้านล่างเป็นเวลาที่แน่นอนและเร็วกว่าอย่างน้อยใน x86-64 และ ARM สำหรับทุกขนาด แต่ใช้รหัสไบนารี่สองเท่าดังนั้นจึงไม่เป็นมิตรกับแคช

มาตรฐานสำหรับรุ่นนี้เทียบกับรุ่นของ alexandrescu ในการประชาสัมพันธ์ของฉันบน Facebook ความเขลาโง่เขลา

ทำงานบนไม่unsignedsigned

inline uint32_t digits10(uint64_t v) {
  return  1
        + (std::uint32_t)(v>=10)
        + (std::uint32_t)(v>=100)
        + (std::uint32_t)(v>=1000)
        + (std::uint32_t)(v>=10000)
        + (std::uint32_t)(v>=100000)
        + (std::uint32_t)(v>=1000000)
        + (std::uint32_t)(v>=10000000)
        + (std::uint32_t)(v>=100000000)
        + (std::uint32_t)(v>=1000000000)
        + (std::uint32_t)(v>=10000000000ull)
        + (std::uint32_t)(v>=100000000000ull)
        + (std::uint32_t)(v>=1000000000000ull)
        + (std::uint32_t)(v>=10000000000000ull)
        + (std::uint32_t)(v>=100000000000000ull)
        + (std::uint32_t)(v>=1000000000000000ull)
        + (std::uint32_t)(v>=10000000000000000ull)
        + (std::uint32_t)(v>=100000000000000000ull)
        + (std::uint32_t)(v>=1000000000000000000ull)
        + (std::uint32_t)(v>=10000000000000000000ull);
}

0

ฉันกำลังทำงานกับโปรแกรมที่ต้องการให้ฉันตรวจสอบว่าผู้ใช้ตอบถูกต้องหรือไม่ว่ามีตัวเลขกี่หลักดังนั้นฉันต้องพัฒนาวิธีการตรวจสอบจำนวนหลักในจำนวนเต็ม มันเป็นเรื่องง่ายที่จะแก้ไข

double check=0, exponent=1000;

while(check<=1)
{
    check=number/pow(10, exponent);
    exponent--;
}

exponent=exponent+2;
cout<<exponent<<endl;

นี่เป็นคำตอบของฉันซึ่งปัจจุบันทำงานกับตัวเลขที่มีตัวเลขน้อยกว่า 10 ^ 1,000 หลัก (สามารถเปลี่ยนแปลงได้โดยการเปลี่ยนค่าเลขชี้กำลัง)

ป.ล. ฉันรู้ว่าคำตอบนี้เป็นเวลาสิบปีที่ผ่านมา แต่ฉันมาที่นี่ในปี 2020 ดังนั้นคนอื่นอาจใช้มัน


-1
template <typename type>
class number_of_decimal_digits {   
    const powers_and_max<type> mPowersAndMax;
public:
    number_of_decimal_digits(){
    }   
    inline size_t ndigits( type i) const {
        if(i<0){
             i += (i == std::numeric_limits<type>::min());
             i=-i;
        }
        const type* begin = &*mPowersAndMax.begin();
        const type* end = begin+mPowersAndMax.size();
        return 1 + std::lower_bound(begin,end,i) - begin;
    }
    inline size_t string_ndigits(const type& i) const {
        return (i<0) + ndigits(i);
    }
    inline size_t operator[](const type& i) const {
       return string_ndigits(i);
    }
};

ที่ที่powers_and_maxเรามีอยู่(10^n)-1สำหรับทุกสิ่งnเช่นนั้น

(10^n) < std::numeric_limits<type>::max()

และstd::numeric_limits<type>::max()ในอาร์เรย์:

template <typename type>
struct powers_and_max : protected std::vector<type>{
    typedef std::vector<type> super;
    using super::const_iterator;
    using super::size;
    type& operator[](size_t i)const{return super::operator[](i)};
    const_iterator begin()const {return super::begin();} 
    const_iterator end()const {return super::end();} 
    powers_and_max() {
       const int size = (int)(log10(double(std::numeric_limits<type>::max())));
       int j = 0;
       type i = 10;
       for( ; j<size ;++j){
           push_back(i-1);//9,99,999,9999 etc;
           i*=10;
       }
       ASSERT(back()<std::numeric_limits<type>::max());
       push_back(std::numeric_limits<type>::max());
   }
};

นี่คือการทดสอบอย่างง่าย:

number_of_decimal_digits<int>  ndd;
ASSERT(ndd[0]==1);
ASSERT(ndd[9]==1);
ASSERT(ndd[10]==2);
ASSERT(ndd[-10]==3);
ASSERT(ndd[-1]==2);
ASSERT(ndd[-9]==2);
ASSERT(ndd[1000000000]==10);
ASSERT(ndd[0x7fffffff]==10);
ASSERT(ndd[-1000000000]==11);
ASSERT(ndd[0x80000000]==11);

แน่นอนว่าอาจใช้การดำเนินการอื่นของชุดที่สั่งซื้อpowers_and_maxและหากมีความรู้ว่าจะมีการจัดกลุ่ม แต่ไม่มีความรู้ในกรณีที่คลัสเตอร์อาจจะใช้การปรับต้นไม้ด้วยตนเองอาจจะดีที่สุด


-1

วิธีที่มีประสิทธิภาพ

int num;
int count = 0;
while(num)
{
   num /= 10;
   ++count;
}

#include <iostream>

int main()
{
   int num;
   std::cin >> num;

   std::cout << "number of digits for " << num << ": ";

   int count = 0;
   while(num)
   {
      num /= 10;
      ++count;
   }

   std::cout << count << '\n';

   return 0;
}

-1

การปรับปรุง C ++ 11 สำหรับโซลูชันที่ต้องการ:

#include <limits>
#include <type_traits>
        template <typename T>
        typename std::enable_if<std::numeric_limits<T>::is_integer, unsigned int>::type
        numberDigits(T value) {
            unsigned int digits = 0;
            if (value < 0) digits = 1;
            while (value) {
                value /= 10;
                ++digits;
            }
            return digits;
        }

ป้องกันการเริ่มต้นแม่แบบด้วย double, et อัล


-1
int numberOfDigits(double number){
    if(number < 0){
        number*=-1;
    }
    int i=0;
        while(number > pow(10, i))
            i++;    
    cout << "This number has " << i << " digits" << endl;
    return i;
}

-2

นี่คือวิธีที่จะทำ:

   int digitcount(int n)
    {
        int count = 1;
        int temp = n;
        while (true)
        {
            temp /= 10;
            if (temp != 0) ++count;
            if (temp == 0) break;
        }

        return count;
    }

2
ในขณะที่กลุ่มอาการ True / break: D
ПетърПетров

-1 นี่เป็นวิธีการเดียวกับที่คำตอบแรกให้เมื่อหกปีก่อนและไม่ได้เพิ่มอะไรเลย

-4

นี่คือวิธีการที่แตกต่าง:

digits = sprintf(numArr, "%d", num);    // where numArr is a char array
if (num < 0)
    digits--;

สิ่งนี้อาจไม่มีประสิทธิภาพ แต่เป็นสิ่งที่แตกต่างจากที่คนอื่นแนะนำ


4
คำขอนั้นมีประสิทธิภาพสูงมาก นี่คือสิ่งที่ตรงกันข้าม
Ira Baxter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.