วิธีที่ถูกต้องในการเปรียบเทียบ System.Double ถึง '0' (a number, int?)


87

ขอโทษนะนี่อาจจะเป็นคำถามโง่ ๆ ง่ายๆ แต่ฉันต้องรู้ให้แน่ใจ

ฉันมีifสำนวนนี้

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

นิพจน์นั้นเท่ากับหรือไม่

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

เหรอ? เพราะงั้นฉันอาจมีปัญหาป้อนifด้วยเช่นค่า 0.9


3
// Comparison of floating point numbers with equality // operator. คุณจำเป็นต้องระบุจริงๆหรือไม่? :)
George Johnston

1
Heck no. มีค่ามากมายระหว่าง 0 ถึง 1 ทำไมไม่ลองทดสอบดูด้วยตัวคุณเองล่ะ
Igby Largeman

12
ฉันเพิ่งเขียนเหมือนกับที่ Resharper ทำเพื่อแสดงว่าโฟกัสของฉันอยู่ที่ไหน
radbyx

@ ชาร์ลส์: นอกจากนี้ยังมีตัวเลขจำนวนมากที่น้อยกว่า 0
Brian

คำตอบ:


116

คุณต้องการให้ค่าเป็น 0 ใกล้แค่ไหน? หากคุณผ่านการดำเนินการจุดลอยตัวจำนวนมากซึ่งใน "ความแม่นยำไม่มีที่สิ้นสุด" อาจส่งผลให้เป็น 0 คุณอาจได้ผลลัพธ์ "ใกล้มาก" ถึง 0

โดยทั่วไปในสถานการณ์นี้คุณต้องการให้ epsilon บางประเภทและตรวจสอบว่าผลลัพธ์อยู่ใน epsilon นั้น:

if (Math.Abs(something) < 0.001)

epsilon ที่คุณควรใช้นั้นขึ้นอยู่กับว่าคุณกำลังทำอะไรอยู่

แน่นอนว่าถ้าผลลัพธ์ควรเป็นศูนย์อย่างแน่นอนการตรวจสอบความเท่าเทียมกันก็ใช้ได้


อันที่จริงฉันต้องการให้มันเป็นศูนย์พอดีมันจะเป็นยังไง? ฉันมองหา Double.Zero แต่ขอให้โชคดี มีค่าคงที่ใช่ไหม ขอบคุณฉันได้รับส่วน epsilon แล้ว :)
radbyx

24
@radbyx: เพียงแค่ใช้== 0. คุณมีตัวอักษรอยู่ที่นั่นซึ่งค่อนข้างคงที่ :)
Jon Skeet

35

หากsomethingได้รับมอบหมายจากผลลัพธ์ของการดำเนินการอื่นนอกเหนือsomething = 0จากนั้นคุณควรใช้:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

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


2
นี้ไม่ได้แก้ปัญหาการปัดเศษเช่นMath.Abs(0.1f - 0.1d) < double.Epsilonเป็นfalse
โทมัสลูล

6
Double.Epsilon มีขนาดเล็กเกินไปสำหรับการเปรียบเทียบเช่นนี้ Double.Epsilon เป็นจำนวนบวกที่น้อยที่สุดที่ double สามารถแทนได้
Evgeni Nabokov

3
สิ่งนี้ไม่สมเหตุสมผลเพราะมันเหมือนกับการเปรียบเทียบกับ
0-1

1
เป้าหมายคือการเปรียบเทียบกับแนวคิดของ 0 โดยไม่ใช้ == อย่างน้อยมันก็ทำให้เกิดความรู้สึกทางคณิตศาสตร์ ฉันสมมติว่าคุณมีสองเท่าในมือและคุณต้องการเปรียบเทียบกับแนวคิดของศูนย์ที่ไม่มี == หากคู่ของคุณแตกต่างจาก 0d ไม่ว่าด้วยเหตุผลใดก็ตามรวมถึงการปัดเศษการเติมทดสอบจะแสดงผลเป็นเท็จ การเปรียบเทียบนี้ดูเหมือนจะใช้ได้สำหรับคู่ใด ๆ และจะคืนค่าจริงก็ต่อเมื่อคู่นี้มีขนาดเล็กที่สุดกว่าจำนวนที่น้อยที่สุดที่สามารถแสดงได้ซึ่งดูเหมือนจะเป็นคำจำกัดความที่ดีสำหรับการทดสอบแนวคิดของ 0 ไม่ใช่?
sonatique

3
@MaurGi: คุณคิดผิด: double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
sonatique

27

ของคุณsomethingคือ a doubleและคุณระบุสิ่งนั้นอย่างถูกต้องในบรรทัด

if (something == 0)

เรามีdoubleทางด้านซ้ายมือ (lhs) และintทางด้านขวามือ (rhs)

แต่ตอนนี้ดูเหมือนว่าคุณคิดว่า lhs จะถูกแปลงเป็น an intแล้ว==เครื่องหมายจะเปรียบเทียบจำนวนเต็มสองจำนวน นั่นไม่ใช่สิ่งที่เกิดขึ้น การแปลงจาก double การ intเป็นที่ชัดเจนและไม่สามารถเกิดขึ้น "โดยอัตโนมัติ"

แต่สิ่งที่ตรงกันข้ามจะเกิดขึ้น rhs จะถูกแปลงเป็นdoubleและจากนั้น==เครื่องหมายจะกลายเป็นการทดสอบความเท่าเทียมกันระหว่างสองคู่ผสม การแปลงนี้เป็นนัย (อัตโนมัติ)

ก็ถือว่าดีกว่า (โดยบางคน) ที่จะเขียน

if (something == 0.0)

หรือ

if (something == 0d)

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

ในบางกรณีก็มีความเกี่ยวข้องเช่นกันที่จะแนะนำ "ความอดทน" เหมือนในคำตอบของ Jon Skeet แต่ความอดทนนั้นก็doubleเกินไป มันสามารถของหลักสูตรจะเป็น1.0ถ้าคุณต้องการ แต่มันไม่จำเป็นต้องเป็น [น้อยในเชิงบวกอย่างเคร่งครัด] จำนวนเต็ม


17

หากคุณต้องการระงับคำเตือนให้ทำดังนี้:

if (something.Equals(0.0))

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


4

ฉันไม่คิดว่ามันเท่าเทียมกันโดยสุจริต ลองพิจารณาตัวอย่างของตัวเอง: something = 0.9 หรือ 0.0004 ในกรณีแรกจะเป็นเท็จในกรณีที่สองจะเป็น TRUE การจัดการกับประเภทนี้ฉันมักจะกำหนดเปอร์เซ็นต์ความแม่นยำให้ฉันและเปรียบเทียบภายในความแม่นยำนั้น ขึ้นอยู่กับความต้องการของคุณ. สิ่งที่ต้องการ...

if(((int)(something*100)) == 0) {


//do something
}

หวังว่านี่จะช่วยได้


2
บางอย่างจะต้องเป็นศูนย์
radbyx

ดังนั้นคุณเป็นคนโชคดีในกรณีนี้ :)
Tigran

3

นี่คือตัวอย่างที่นำเสนอปัญหา (จัดทำใน LinQPad - หากคุณไม่มีให้ใช้Console.WritelineแทนDumpวิธีการ):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

ทั้ง x และ y มีความเหมือนกันในทางทฤษฎีและเท่ากับ 0.00001 แต่เนื่องจากการขาด "ความแม่นยำไม่สิ้นสุด" ค่าเหล่านี้จึงแตกต่างกันเล็กน้อย น่าเสียดายเล็กน้อยพอที่จะกลับมาfalseเมื่อเทียบกับ 0 ตามปกติ

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