เป็นการดีหรือไม่ที่จะแทนที่การหารด้วยการคูณเมื่อทำได้


73

เมื่อใดก็ตามที่ฉันต้องการการแบ่งตัวอย่างเช่นการตรวจสอบเงื่อนไขฉันต้องการ refactor การแสดงออกของการหารเป็นการคูณ:

รุ่นเดิม:

if(newValue / oldValue >= SOME_CONSTANT)

เวอร์ชั่นใหม่:

if(newValue >= oldValue * SOME_CONSTANT)

เพราะฉันคิดว่ามันสามารถหลีกเลี่ยง:

  1. การหารด้วยศูนย์

  2. ล้นเมื่อoldValueมีขนาดเล็กมาก

นั่นถูกต้องใช่ไหม? มีปัญหาสำหรับนิสัยนี้หรือไม่?


41
ระวังด้วยตัวเลขลบทั้งสองเวอร์ชันจะตรวจสอบสิ่งต่าง ๆ โดยสิ้นเชิง คุณแน่ใจoldValue >= 0หรือไม่
user2313067

37
ขึ้นอยู่กับภาษา (แต่ที่สำคัญที่สุดคือ C) การปรับให้เหมาะสมที่สุดที่คุณนึกถึงคอมไพเลอร์สามารถทำได้ดีกว่า- หรือ -มีความรู้สึกมากพอที่จะไม่ทำเลย
Mark Benningfield

63
มันไม่เคยเป็น "แนวปฏิบัติที่ดี" ที่จะแทนที่โค้ด X ด้วยโค้ด Y ทุกครั้งเมื่อ X และ Y ไม่เทียบเท่ากันทางอรรถศาสตร์ แต่มันเป็นความคิดที่ดีเสมอที่จะดู X และ Y เปิดสมองคิดเกี่ยวกับความต้องการและตัดสินใจว่าตัวเลือกใดที่ถูกต้องมากขึ้น และหลังจากนั้นคุณควรคิดด้วยว่าการทดสอบใดที่ต้องใช้เพื่อยืนยันว่าคุณมีความแตกต่างทางความหมายที่ถูกต้อง
Doc Brown

12
@ MarkBenningfield: ไม่ว่าอะไรก็ตามคอมไพเลอร์ไม่สามารถปรับการหารด้วยศูนย์ได้ "การเพิ่มประสิทธิภาพ" ที่คุณคิดว่าเป็น "การเพิ่มประสิทธิภาพความเร็ว" OP กำลังพิจารณาเกี่ยวกับการเพิ่มประสิทธิภาพประเภทอื่น - การหลีกเลี่ยงข้อผิดพลาด
slebetman

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

คำตอบ:


74

สองกรณีทั่วไปที่ควรพิจารณา:

เลขจำนวนเต็ม

เห็นได้ชัดว่าหากคุณใช้เลขคณิตเลขจำนวนเต็ม (ซึ่งตัดปลาย) คุณจะได้รับผลลัพธ์ที่แตกต่างกัน นี่คือตัวอย่างเล็ก ๆ ใน C #:

public static void TestIntegerArithmetic()
{
    int newValue = 101;
    int oldValue = 10;
    int SOME_CONSTANT = 10;

    if(newValue / oldValue > SOME_CONSTANT)
    {
        Console.WriteLine("First comparison says it's bigger.");
    }
    else
    {
        Console.WriteLine("First comparison says it's not bigger.");
    }

    if(newValue > oldValue * SOME_CONSTANT)
    {
        Console.WriteLine("Second comparison says it's bigger.");
    }
    else
    {
        Console.WriteLine("Second comparison says it's not bigger.");
    }
}

เอาท์พุท:

First comparison says it's not bigger.
Second comparison says it's bigger.

เลขทศนิยม

นอกเหนือจากความจริงที่ว่าการหารสามารถให้ผลลัพธ์ที่แตกต่างกันเมื่อมันหารด้วยศูนย์ (มันสร้างข้อยกเว้นในขณะที่การคูณไม่ได้) มันยังสามารถส่งผลให้เกิดข้อผิดพลาดในการปัดเศษที่แตกต่างกันเล็กน้อย ตัวอย่างง่าย ๆ ใน C #:

public static void TestFloatingPoint()
{
    double newValue = 1;
    double oldValue = 3;
    double SOME_CONSTANT = 0.33333333333333335;

    if(newValue / oldValue >= SOME_CONSTANT)
    {
        Console.WriteLine("First comparison says it's bigger.");
    }
    else
    {
        Console.WriteLine("First comparison says it's not bigger.");
    }

    if(newValue >= oldValue * SOME_CONSTANT)
    {
        Console.WriteLine("Second comparison says it's bigger.");
    }
    else
    {
        Console.WriteLine("Second comparison says it's not bigger.");
    }
}

เอาท์พุท:

First comparison says it's not bigger.
Second comparison says it's bigger.

ในกรณีที่คุณไม่เชื่อฉันนี่คือซอที่คุณสามารถปฏิบัติและดูด้วยตัวคุณเอง

ภาษาอื่นอาจแตกต่างกัน โปรดจำไว้ว่า C # เช่นหลายภาษาใช้ไลบรารีจุดลอยตัวมาตรฐาน IEEE (IEEE 754)ดังนั้นคุณควรได้รับผลลัพธ์เดียวกันในเวลารันมาตรฐานอื่น ๆ

ข้อสรุป

หากคุณกำลังทำงานกรีนฟิลด์คุณอาจจะโอเค

หากคุณกำลังทำงานกับรหัสดั้งเดิมและแอปพลิเคชันเป็นแอปพลิเคชั่นทางการเงินหรืออื่น ๆ ที่มีความละเอียดอ่อนซึ่งดำเนินการทางคณิตศาสตร์และจำเป็นต้องให้ผลลัพธ์ที่สอดคล้องกันให้ระมัดระวังเป็นอย่างมาก หากคุณต้องตรวจสอบให้แน่ใจว่าคุณมีการทดสอบหน่วยที่จะตรวจจับการเปลี่ยนแปลงเล็กน้อยในคณิตศาสตร์

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

หากคุณใช้อัลกอริทึมกับสเปคฉันจะไม่เปลี่ยนแปลงอะไรเลยไม่ใช่เพียงเพราะปัญหาการปัดเศษข้อผิดพลาด แต่เพื่อให้นักพัฒนาสามารถตรวจสอบรหัสและแมปนิพจน์แต่ละตัวกลับไปสเปคเพื่อให้แน่ใจว่าไม่มีการใช้งาน ข้อบกพร่อง.


41
ประการที่สองบิตทางการเงิน สวิตช์ชนิดนี้กำลังขอให้นักบัญชีไล่คุณไปสู่พิทช์ ฉันจำ 5,000 เส้นที่ฉันต้องใช้ความพยายามมากขึ้นในการรักษาโกยที่อ่าวมากกว่าในการหาคำตอบที่ "ถูกต้อง" - ซึ่งโดยทั่วไปแล้วผิดเล็กน้อย การถูกออกโดย. 01% นั้นไม่สำคัญเลย ดังนั้นฉันถูกบังคับให้ทำการคำนวณในทางที่ทำให้เกิดข้อผิดพลาดในการปัดเศษอย่างเป็นระบบ
Loren Pechtel

8
ลองนึกถึงการซื้อขนม 5 เซ็นต์ (ไม่ใช่ของที่มีอยู่แล้ว) ซื้อ 20 ชิ้นคำตอบ "ถูกต้อง" ไม่ต้องเสียภาษีเพราะไม่มีภาษีสำหรับการซื้อ 20 ชิ้นในหนึ่งชิ้น
Loren Pechtel

24
@ LorenPechtel นั่นเป็นเพราะระบบภาษีส่วนใหญ่มีกฎ (ด้วยเหตุผลที่ชัดเจน) ว่าภาษีถูกเรียกเก็บต่อการทำธุรกรรมและภาษีมีกำหนดเพิ่มขึ้นไม่น้อยไปกว่าเหรียญที่เล็กที่สุดของอาณาจักรและจำนวนเงินจะถูกปัดเศษลง กฎเหล่านั้น "ถูกต้อง" เพราะถูกต้องตามกฎหมายและสอดคล้องกัน นักบัญชีที่มีโกยอาจจะรู้ว่ากฎจริงๆในลักษณะที่โปรแกรมเมอร์คอมพิวเตอร์จะไม่ (เว้นแต่พวกเขาเป็นนักบัญชีที่มีประสบการณ์) ข้อผิดพลาด 0.01% อาจทำให้เกิดข้อผิดพลาดในการปรับสมดุลและเป็นเรื่องผิดกฎหมายที่จะมีข้อผิดพลาดในการปรับสมดุล
Steve

9
เพราะฉันไม่เคยได้ยินคำว่ากรีนฟิลด์มาก่อนฉันจึงค้นหา Wikipedia กล่าวว่าเป็น "โครงการที่ไม่มีข้อ จำกัด ใด ๆ จากการทำงานก่อนหน้านี้"
Henrik Ripa

9
@Steve: เมื่อเร็ว ๆ นี้เจ้านายของฉันเปรียบเทียบ "กรีนฟิลด์" กับ "บราวน์ฟิลด์" ฉันตั้งข้อสังเกตว่าบางโครงการเป็นเหมือน "แบล็กฟิลด์" ... :-D
DevSolar

25

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

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

ของคุณถูกต้องว่าเวอร์ชั่นใหม่หลีกเลี่ยงการหารด้วยศูนย์ แน่นอนคุณไม่จำเป็นต้องเพิ่มตัวป้องกัน (ตามแนวของif (oldValue != 0)) แต่นี่สมเหตุสมผลไหม เวอร์ชันเก่าของคุณแสดงอัตราส่วนระหว่างตัวเลขสองตัว หากตัวหารเป็นศูนย์แสดงว่าคุณไม่ได้กำหนดอัตราส่วน สิ่งนี้อาจมีความหมายมากกว่าในสถานการณ์ของคุณเช่น คุณไม่ควรให้ผลลัพธ์ในกรณีนี้

การป้องกันการล้นนั้นเป็นที่ถกเถียงกัน หากคุณรู้ว่าnewValueมันใหญ่กว่าเสมอoldValueบางทีคุณก็สามารถโต้แย้งได้ อย่างไรก็ตามอาจมีกรณีที่(oldValue * SOME_CONSTANT)จะล้น ดังนั้นฉันไม่เห็นกำไรมากที่นี่

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

โดยภาพรวมทั้งหมดข้างต้นโดยทั่วไปฉันไม่คิดว่าจะมีเวอร์ชั่นใหม่ของคุณมากนักเมื่อเทียบกับเวอร์ชั่นเก่าโดยเฉพาะเมื่อลดความชัดเจนลง อย่างไรก็ตามอาจมีบางกรณีที่มีประโยชน์บางอย่าง


16
เอ๊ะการคูณโดยพลการนั้นมีประสิทธิภาพมากกว่าการแบ่งตามอำเภอใจไม่ได้ขึ้นอยู่กับตัวประมวลผลสำหรับเครื่องจริง
Deduplicator

1
นอกจากนี้ยังมีปัญหาของเลขคณิตเลขจำนวนเต็มเทียบกับทศนิยม หากอัตราส่วนเป็นเศษส่วนหน่วยจะต้องดำเนินการในจุดลอยตัวที่ต้องใช้นักแสดง การพลาดนักแสดงจะทำให้เกิดข้อผิดพลาดโดยไม่ตั้งใจ หากเศษส่วนเกิดขึ้นเป็นอัตราส่วนระหว่างจำนวนเต็มสองจำนวนเล็กการจัดเรียงใหม่จะอนุญาตให้ทำการเปรียบเทียบในเลขคณิตจำนวนเต็ม (จุดที่ข้อโต้แย้งของคุณจะนำไปใช้.)
rwong

@rwong ไม่เสมอไป หลายภาษามีการหารจำนวนเต็มโดยการวางส่วนทศนิยมดังนั้นจึงไม่จำเป็นต้องใช้การร่าย
ต. Sar

@ T.Sar เทคนิคที่คุณอธิบายและความหมายที่อธิบายไว้ในคำตอบนั้นแตกต่างกัน ซีแมนทิกส์คือผู้เขียนโปรแกรมตั้งใจจะให้ค่าทศนิยมหรือเศษส่วนหรือไม่ เทคนิคที่คุณอธิบายคือการหารด้วยการคูณซึ่งกันและกันซึ่งบางครั้งเป็นการประมาณที่สมบูรณ์แบบ (การทดแทน) สำหรับการหารจำนวนเต็ม เทคนิคหลังมักใช้เมื่อตัวหารรู้จักล่วงหน้าเนื่องจากการสืบทอดของจำนวนเต็มซึ่งกันและกัน (shifted ด้วย 2 ** 32) สามารถทำได้ในเวลาคอมไพล์ การทำเช่นนั้นที่ runtime จะไม่เป็นประโยชน์เพราะมันแพงกว่า CPU
วงเวียน

22

เลขที่

ผมอาจจะเรียกว่าการเพิ่มประสิทธิภาพก่อนวัยอันควรในความหมายกว้างโดยไม่คำนึงถึงว่าคุณกำลังเพิ่มประสิทธิภาพสำหรับผลการดำเนินงานเป็นวลีที่โดยทั่วไปหมายถึงหรือสิ่งอื่นที่สามารถเพิ่มประสิทธิภาพเช่นขอบนับ , สายรหัสหรือ ยิ่งกว้างขึ้นสิ่งต่าง ๆ เช่น"การออกแบบ"

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

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

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

หมายเหตุ:คอมไพเลอร์มักจะเพิ่มประสิทธิภาพการแบ่งสำหรับคุณต่อไป - เมื่อปลอดภัยที่จะทำ


11
-1 คำตอบนี้ไม่เหมาะกับคำถามซึ่งเกี่ยวกับข้อผิดพลาดที่อาจเกิดขึ้นของการหาร - ไม่มีอะไรเกี่ยวข้องกับการปรับให้เหมาะสม
เบ็

13
@ BenCottrell มันลงตัวพอดี ข้อผิดพลาดอยู่ในการวางค่าในการเพิ่มประสิทธิภาพการทำงานที่ไม่มีจุดหมายที่ค่าใช้จ่ายของการบำรุงรักษา จากคำถาม"มีปัญหาสำหรับนิสัยนี้ไหม" - ใช่ มันจะนำไปสู่การเขียนซึ่งพูดพล่อยๆอย่างรวดเร็ว
Michael

9
@Michael คำถามไม่ได้ถามเกี่ยวกับสิ่งเหล่านั้นอย่างใดอย่างหนึ่ง - มันเป็นการถามเฉพาะเกี่ยวกับความถูกต้องของสองนิพจน์ที่ต่างกันซึ่งแต่ละอันมีความหมายและพฤติกรรมที่แตกต่างกัน
Ben Cottrell

5
@ BenCottrell บางทีคุณสามารถชี้ให้ฉันเห็นว่าในคำถามมีใครพูดถึงสิ่งใดบ้างเกี่ยวกับความถูกต้อง?
Michael

5
@ BenCottrell คุณน่าจะพูดว่า 'ฉันทำไม่ได้' :)
Michael

13

ใช้อย่างใดอย่างหนึ่งเป็นรถม้าชนิดหนึ่งน้อยและทำให้มีเหตุผลมากขึ้น

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

นี่คือตัวอย่างที่จะแสดงว่าขึ้นอยู่กับสถานการณ์:

ส่วนที่ดี:

if ((ptr2 - ptr1) >= n / 3)  // good: check if length of subarray is at least n/3
    ...

การคูณที่ไม่ดี:

if ((ptr2 - ptr1) * 3 >= n)  // bad: confusing!! what is the intention of this code?
    ...

การคูณที่ดี:

if (j - i >= 2 * min_length)  // good: obviously checking for a minimum length
    ...

กองไม่ดี:

if ((j - i) / 2 >= min_length)  // bad: confusing!! what is the intention of this code?
    ...

การคูณที่ดี:

if (new_length >= old_length * 1.5)  // good: is the new size at least 50% bigger?
    ...

กองไม่ดี:

if (new_length / old_length >= 2)  // bad: BUGGY!! will fail if old_length = 0!
    ...

2
ฉันยอมรับว่ามันขึ้นอยู่กับบริบท แต่ตัวอย่างสองคู่แรกของคุณนั้นแย่มาก ฉันจะไม่ชอบอีกอันหนึ่งไม่ว่าจะในกรณีใด
Michael

6
@Michael: เอ่อ ... คุณคิดว่า(ptr2 - ptr1) * 3 >= nมันง่ายที่จะเข้าใจเหมือนการแสดงออกptr2 - ptr1 >= n / 3? มันไม่ได้ทำให้สมองคุณสะดุดและพยายามที่จะถอดรหัสความหมายของการเพิ่มความแตกต่างระหว่างตัวชี้สองตัว? ถ้าคุณและทีมของคุณเห็นได้ชัดเจนจริงๆแล้วฉันคิดว่าคุณมีพลังมากขึ้น ฉันจะต้องอยู่ในชนกลุ่มน้อยที่ช้า
Mehrdad

2
ตัวแปรที่เรียกว่าnและหมายเลขโดยพลการ 3 นั้นทำให้เกิดความสับสนในทั้งสองกรณี แต่แทนที่ด้วยชื่อที่สมเหตุสมผลฉันไม่พบว่าจะสับสนมากกว่าอีก
Michael

1
ตัวอย่างเหล่านี้ไม่ได้แย่จริงๆ .. ไม่ใช่ 'แย่มาก' - แม้ว่าคุณจะย่อยใน 'ชื่อที่สมเหตุสมผล' พวกเขายังคงมีเหตุผลน้อยลงเมื่อคุณเปลี่ยนพวกเขาสำหรับกรณีที่ไม่ดี ถ้าฉันยังใหม่ต่อโครงการฉันอยากเห็นกรณี 'ดี' ที่ระบุไว้ในคำตอบนี้เมื่อฉันไปแก้ไขรหัสการผลิต
John-M

3

การทำอะไรที่ “ เป็นไปได้” นั้นเป็นความคิดที่ไม่ค่อยดีนัก

ความสำคัญอันดับหนึ่งของคุณควรมีความถูกต้องตามด้วยความสามารถในการอ่านและการบำรุงรักษา การแทนที่การสุ่มสี่สุ่มห้าด้วยการทวีคูณเมื่อใดก็ตามที่เป็นไปได้มักจะล้มเหลวในแผนกความถูกต้องบางครั้งก็หายากและยากที่จะหาเคส

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


1

เกี่ยวกับความสามารถในการอ่านรหัสฉันคิดว่าการคูณนั้นสามารถอ่านได้ง่ายขึ้นในบางกรณี ตัวอย่างเช่นหากมีบางสิ่งที่คุณต้องตรวจสอบว่าnewValueเพิ่มขึ้น 5 เปอร์เซ็นต์หรือมากกว่าoldValueนั้น1.05 * oldValueแสดงว่าเป็นเกณฑ์ที่ใช้ทดสอบnewValueและเป็นเรื่องธรรมดาที่จะเขียน

    if (newValue >= 1.05 * oldValue)

แต่ระวังจำนวนลบเมื่อคุณปรับโครงสร้างสิ่งนี้ด้วยวิธีนี้ (แทนที่การหารด้วยการคูณหรือแทนที่การคูณด้วยการหาร) เงื่อนไขสองข้อที่คุณถือว่าเทียบเท่าหากoldValueรับประกันว่าจะไม่เป็นลบ แต่สมมติว่าnewValueเป็นจริง -13.5 และoldValue-10.1 แล้วก็

newValue/oldValue >= 1.05

ประเมินเป็นจริงแต่

newValue >= 1.05 * oldValue

ประเมินเท็จ


1

หมายเหตุกระดาษที่มีชื่อเสียงหารด้วยจำนวนเต็มคงใช้คูณ

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

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

ฉันสนใจเกี่ยวกับการเพิ่มประสิทธิภาพขนาดเล็กดังนั้นฉันอาจจะดูความเป็นไปได้การเพิ่มประสิทธิภาพ

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

นอกจากนี้บนสถาปัตยกรรม 32 บิตส่วน 64 บิตไม่เหมาะสมที่ผมพบ


1

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

และในทางกลับกันจะเกิดอะไรขึ้นถ้าoldValueมีขนาดใหญ่มาก คุณมีปัญหาเดียวกันเพียงรอบที่ตรงกันข้าม

หากคุณต้องการหลีกเลี่ยง (หรือลด) ความเสี่ยงของการล้น / อันเดอร์วิธีที่ดีที่สุดคือการตรวจสอบว่าnewValueมีขนาดใกล้เคียงที่สุดoldValueหรือSOME_CONSTANTมากที่สุดหรือไม่ จากนั้นคุณสามารถเลือกการดำเนินการหารที่เหมาะสมได้เช่นกัน

    if(newValue / oldValue >= SOME_CONSTANT)

หรือ

    if(newValue / SOME_CONSTANT >= oldValue)

และผลลัพธ์จะแม่นยำที่สุด

สำหรับการหารด้วยศูนย์ในประสบการณ์ของฉันนี้เกือบจะไม่เหมาะสมที่จะ "แก้ไข" ในวิชาคณิตศาสตร์ หากคุณมีการหารด้วยศูนย์ในการตรวจสอบอย่างต่อเนื่องของคุณแล้วแน่นอนว่าคุณมีสถานการณ์ที่ต้องใช้การวิเคราะห์และการคำนวณใด ๆ ที่ยึดตามข้อมูลนี้จะไม่มีความหมาย การตรวจสอบหารด้วยศูนย์อย่างชัดเจนเป็นการย้ายที่เหมาะสมเกือบตลอดเวลา (โปรดทราบว่าฉันพูดว่า "เกือบ" ที่นี่เพราะฉันไม่ได้อ้างว่าผิดพลาดฉันจะทราบว่าฉันจำไม่ได้ว่าเห็นเหตุผลที่ดีสำหรับสิ่งนี้ใน 20 ปีของการเขียนซอฟต์แวร์ฝังตัวและเดินหน้าต่อไป .)

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

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


0

ห่อหุ้มเงื่อนไขทางคณิตศาสตร์ในวิธีการและคุณสมบัติที่มีความหมาย การตั้งชื่อที่ดีไม่เพียง แต่จะบอกคุณว่า "A / B" หมายถึงอะไรการตรวจสอบพารามิเตอร์และการจัดการข้อผิดพลาดก็สามารถซ่อนอยู่ในนั้นได้อย่างเรียบร้อยเช่นกัน

ที่สำคัญเนื่องจากวิธีการเหล่านี้ประกอบด้วยตรรกะที่ซับซ้อนมากขึ้นความซับซ้อนภายนอกยังคงจัดการได้ง่ายมาก

ฉันว่าการทดแทนการคูณเป็นวิธีแก้ปัญหาที่สมเหตุสมผลเพราะปัญหาไม่ชัดเจน


0

ฉันคิดว่ามันเป็นความคิดที่ไม่ดีที่จะแทนที่การคูณด้วยหน่วยงานเนื่องจาก CPU ของ ALU (Arithmetic-Logic Unit) ดำเนินการตามขั้นตอนวิธีแม้ว่ามันจะถูกนำไปใช้ในฮาร์ดแวร์ เทคนิคที่ซับซ้อนกว่านี้มีอยู่ในโปรเซสเซอร์รุ่นใหม่ โดยทั่วไปโปรเซสเซอร์มุ่งมั่นที่จะขนานการทำงานของบิตคู่เพื่อลดรอบสัญญาณนาฬิกาที่ต้องการ อัลกอริธึมการคูณสามารถขนานกันได้อย่างมีประสิทธิภาพ (แม้ว่าจะต้องมีทรานซิสเตอร์เพิ่ม) อัลกอริธึมการหารไม่สามารถขนานได้อย่างมีประสิทธิภาพ อัลกอริธึมการแบ่งที่มีประสิทธิภาพมากที่สุดนั้นค่อนข้างซับซ้อน โดยทั่วไปพวกเขาต้องการรอบสัญญาณนาฬิกาต่อบิตมากขึ้น

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