มีอะไรผิดปกติกับการใช้ == เพื่อเปรียบเทียบลอยใน Java?


177

ตามหน้า java.sun นี้ ==คือโอเปอเรเตอร์การเปรียบเทียบความเท่าเทียมกันสำหรับหมายเลขทศนิยมใน Java

อย่างไรก็ตามเมื่อฉันพิมพ์รหัสนี้:

if(sectionID == currentSectionID)

ในเครื่องมือแก้ไขของฉันและเรียกใช้การวิเคราะห์แบบสแตติกฉันได้รับ: "JAVA0078 ค่าทศนิยมเมื่อเทียบกับ =="

เกิดอะไรขึ้นกับการใช้==เพื่อเปรียบเทียบค่าทศนิยม วิธีที่ถูกต้องในการทำคืออะไร? 


29
เนื่องจากการเปรียบเทียบลอยกับ == เป็นปัญหาจึงไม่ฉลาดที่จะใช้มันเป็นรหัส ชื่อในรหัสตัวอย่างของคุณแนะนำว่าคุณกำลังทำอะไรอยู่ แนะนำให้ใช้จำนวนเต็มแบบยาว (ยาว) และเป็นมาตรฐานสำหรับ ID
คาร์ล Manaster


4
ใช่นั่นเป็นเพียงตัวอย่างแบบสุ่มหรือคุณใช้ลอยเป็น ID จริงหรือ มีเหตุผลหรือไม่?
ต่อ Wiklander

5
"สำหรับเขตข้อมูลโฟลตใช้วิธี Float.compare และสำหรับฟิลด์คู่ใช้ Double.compare การรักษาแบบพิเศษสำหรับฟิลด์โฟลตและฟิลด์คู่จำเป็นโดยการมีอยู่ของ Float.NaN, -0.0f และค่าคงที่คู่แบบอะนาล็อก ดูเอกสารประกอบ Float.equals " (โจชัวโบลช: Java ที่มีประสิทธิภาพ)
lbalazscs

คำตอบ:


211

วิธีที่ถูกต้องในการทดสอบการลอยสำหรับ 'ความเท่าเทียมกัน' คือ:

if(Math.abs(sectionID - currentSectionID) < epsilon)

โดยที่ epsilon มีจำนวนน้อยมากเช่น 0.00000001 ขึ้นอยู่กับความแม่นยำที่ต้องการ


26
ดูลิงก์ในคำตอบที่ยอมรับได้ ( cygnus-software.com/papers/comparingfloats/comparingfloats.htm ) เพราะเหตุใด epsilon แบบคงที่จึงไม่ใช่ความคิดที่ดีเสมอไป โดยเฉพาะอย่างยิ่งเมื่อค่าในการลอยตัวมีขนาดใหญ่ขึ้น (หรือเล็ก) เอปไซลอนจะไม่เหมาะสมอีกต่อไป (การใช้ epsilon นั้นใช้ได้ถ้าคุณรู้ว่าค่าทศนิยมของคุณนั้นค่อนข้างสมเหตุสมผล)
PT

1
@PT เขาสามารถคูณ epsilon ด้วยตัวเลขหนึ่งตัวและเปลี่ยนฟังก์ชั่นif(Math.abs(sectionID - currentSectionID) < epsilon*sectionIDเป็นจัดการปัญหานั้นได้หรือไม่
กระตือรือร้น

3
นี่อาจเป็นคำตอบที่ดีที่สุด แต่ก็ยังมีข้อบกพร่อง คุณได้รับเอปไซลอนมาจากไหน
Michael Piefel

1
@MichaelPiefel พูดแล้ว: "ขึ้นอยู่กับความแม่นยำที่ต้องการ" การลอยตัวตามธรรมชาตินั้นมีคุณค่าทางกายภาพเหมือนกับ: คุณสนใจเฉพาะตำแหน่งที่ จำกัด บางอย่างขึ้นอยู่กับความไม่ถูกต้องทั้งหมดความแตกต่างใด ๆ
ivan_pozdeev

แต่ OP ต้องการเพียงเพื่อทดสอบความเท่าเทียมกันเท่านั้นและเนื่องจากเป็นที่ทราบกันดีว่าไม่น่าเชื่อถือจึงต้องใช้วิธีการอื่น ถึงกระนั้นฉันก็ไม่เข้าใจเขารู้ว่า "ความแม่นยำที่ต้องการ" ของเขาคืออะไร ดังนั้นหากคุณต้องการทดสอบความเท่าเทียมกันที่เชื่อถือได้มากขึ้นคำถามก็ยังคงอยู่: คุณจะได้รับเอปไซลอนจากที่ไหน ฉันเสนอให้ใช้Math.ulp()ในคำตอบของคำถามนี้
Michael Piefel

53

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

นี้บทความเกี่ยวกับการลงทะเบียนจะช่วยให้ภาพรวมที่ดีว่าทำไมเป็นกรณีนี้; การอ่านที่มีประโยชน์และน่าสนใจ


@kevindtimm: ดังนั้นคุณจะทำแบบทดสอบความเสมอภาคของคุณถ้าอย่างนั้นถ้า (หมายเลข == 6.099999904632568359375) ทุกครั้งที่คุณต้องการทราบจำนวนเท่ากับ 6.1 ... ใช่คุณถูกต้อง ... ทุกอย่างในคอมพิวเตอร์มีความเข้มงวด เพียงว่าการประมาณค่าที่ใช้สำหรับการลอยตัวนั้นเป็นแบบเคาน์เตอร์อย่างง่าย ๆ เมื่อทำการแก้ไขปัญหาทางคณิตศาสตร์
Newtopian

ค่าจุดลอยตัวเป็นเพียงค่าที่ไม่ชัดเจนของฮาร์ดแวร์เฉพาะเท่านั้น
Stuart P. Bentley

1
@ Stuart ฉันเข้าใจผิด แต่ฉันไม่คิดว่า bug FDIV นั้นไม่ได้กำหนดขึ้น คำตอบที่ได้รับจากฮาร์ดแวร์ไม่เป็นไปตามข้อกำหนด แต่ก็กำหนดไว้ได้ว่าการคำนวณแบบเดียวกันนั้นให้ผลลัพธ์ที่ไม่ถูกต้องเหมือนกันเสมอ
Gravity

@ Gravity คุณสามารถยืนยันว่าพฤติกรรมใด ๆ ถูกกำหนดให้เป็นชุดของคำเตือนเฉพาะ
Stuart P. Bentley

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

22

เพียงให้เหตุผลเบื้องหลังสิ่งที่คนอื่นพูด

การแสดงเลขฐานสองของโฟลตนั้นน่ารำคาญ

ในไบนารีโปรแกรมเมอร์ส่วนใหญ่ทราบความสัมพันธ์ระหว่าง 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d

มันก็ทำงานได้ดีเหมือนกัน

.1b = .5d, .01b = .25d, .001b = .125, ...

ปัญหาคือไม่มีวิธีที่แน่นอนในการแสดงตัวเลขทศนิยมส่วนใหญ่เช่น. 1, .2, .3, ฯลฯ สิ่งที่คุณสามารถทำได้คือประมาณในไบนารี ระบบจะปัดเศษเหลวไหลเล็กน้อยเมื่อพิมพ์ตัวเลขเพื่อให้แสดงเป็น. 1 แทนที่จะเป็น. 10000000000001 หรือ. 99999999999999 (ซึ่งอาจใกล้เคียงกับการนำเสนอที่เก็บไว้เป็น. 1)

แก้ไขจากความคิดเห็น: สาเหตุของปัญหานี้คือความคาดหวังของเรา เราคาดหวังอย่างเต็มที่ว่า 2/3 จะได้รับความอับอายในบางจุดเมื่อเราแปลงเป็นทศนิยมทั้ง. 7 หรือ. 67 หรือ. 666667 .. แต่เราไม่คาดหวังว่า. 1 จะถูกปัดเศษโดยอัตโนมัติในลักษณะเดียวกับ 2/3 - และนั่นคือสิ่งที่เกิดขึ้น

อย่างไรก็ตามถ้าคุณอยากรู้ว่าจำนวนที่เก็บไว้ภายในนั้นเป็นเลขฐานสองที่แท้จริงโดยใช้ไบนารี "สัญลักษณ์ทางวิทยาศาสตร์" ดังนั้นถ้าคุณบอกให้เก็บเลขทศนิยม 10.75d มันจะเก็บ 1010b สำหรับ 10 และ 0.11b สำหรับทศนิยม ดังนั้นมันจะเก็บ. 101011 จากนั้นจะบันทึกไม่กี่บิตในตอนท้ายเพื่อพูดว่า: เลื่อนจุดทศนิยมไปทางขวาสี่ตำแหน่ง

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


1
@ Matt K - อืมไม่ใช่จุดคงที่ หากคุณ "บันทึกสองสามบิตในตอนท้ายเพื่อพูดให้ย้ายจุดทศนิยมไปทางขวา" นั่นคือทศนิยม จุดคงที่ใช้ตำแหน่งของจุดฐานเป็นอย่างดีคงที่ นอกจากนี้โดยทั่วไปเนื่องจากการเปลี่ยนจุด binamal (?) สามารถทำให้คุณมี '1' อยู่ในตำแหน่งซ้ายสุดคุณจะพบระบบบางระบบที่ละเว้น '1' นำหน้าการจัดสรรพื้นที่จึงเป็นไท (1) bit!) เพื่อขยายขอบเขตของเลขชี้กำลัง
JustJeff

ปัญหาไม่เกี่ยวข้องกับการแทนค่าฐานสองกับฐานสิบ ด้วยทศนิยมทศนิยมคุณยังมีสิ่งต่าง ๆ เช่น (1/3) * 3 == 0.99999999999999999999999999999999
dan04

2
@ dan04 ใช่เนื่องจาก 1/3 ไม่มีการแทนทศนิยมหรือไบนารีมันมีการแทนแบบ trinary และจะแปลงอย่างถูกต้องด้วยวิธีนี้ :) ตัวเลขที่ฉันแสดงรายการ (.1, .25, ฯลฯ ) ทั้งหมดมีการแทนทศนิยมที่สมบูรณ์แบบ แต่ไม่มีการแทนฐานสอง - และผู้คนจะคุ้นเคยกับผู้ที่มีการแสดง "แน่นอน" BCD จะจัดการได้อย่างสมบูรณ์แบบ นั่นคือความแตกต่าง
Bill K

1
สิ่งนี้ควรมี upvotes มากขึ้นเนื่องจากจะอธิบายปัญหาจริงที่อยู่เบื้องหลังปัญหา
Levite

19

เกิดอะไรขึ้นกับการใช้ == เพื่อเปรียบเทียบค่าทศนิยม

เพราะมันไม่จริงเลย 0.1 + 0.2 == 0.3


7
แล้วFloat.compare(0.1f+0.2f, 0.3f) == 0ไงล่ะ
อำนาจกุมภ์

0.1f + 0.2f == 0.3f แต่ 0.1d + 0.2d! = 0.3d โดยค่าเริ่มต้น 0.1 + 0.2 เป็นสองเท่า 0.3 เป็นสองเท่าเช่นกัน
burnabyRails

12

ฉันคิดว่ามีความสับสนรอบ ๆ ลอยตัว (และเพิ่มเป็นสองเท่า) มันเป็นการดีที่จะเคลียร์มัน

  1. ไม่มีอะไรผิดปกติในการใช้ลอยเป็น ID ใน JVM [*] ที่เป็นไปตามมาตรฐาน หากคุณเพียงแค่ตั้งรหัสลอยตัวเป็น x ไม่ต้องทำอะไรเลย (เช่นไม่มีเลขคณิต) และทดสอบในภายหลังสำหรับ y == x คุณจะไม่เป็นไร นอกจากนี้ยังมีอะไรผิดปกติในการใช้พวกเขาเป็นกุญแจสำคัญใน HashMap สิ่งที่คุณไม่สามารถทำได้คือสมมติว่ามีความเท่าเทียมกันเช่นx == (x - y) + yนี้ผู้คนมักจะใช้ชนิดจำนวนเต็มเป็น ID และคุณสามารถสังเกตได้ว่าคนส่วนใหญ่ที่นี่ถูกถอดออกโดยรหัสนี้ดังนั้นสำหรับเหตุผลในทางปฏิบัติมันจะดีกว่า . ทราบว่ามีที่แตกต่างกันหลายdoubleค่าที่มีความยาวเพื่อให้คุณได้รับอะไรโดยใช้values doubleนอกจากนี้การสร้าง "รหัสที่พร้อมใช้งานต่อไป" อาจยุ่งยากด้วยการเพิ่มเป็นสองเท่าและต้องการความรู้เกี่ยวกับคณิตศาสตร์เลขทศนิยม ไม่คุ้มกับปัญหา

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

[*] เมื่อฉันพูดว่า "มาตรฐาน JVM" ฉันต้องการยกเว้นการใช้งาน JVM ที่เสียหายจากสมอง ดูนี่สิ


เมื่อใช้การลอยเป็น ID ต้องใช้ความระมัดระวังเพื่อให้แน่ใจว่ามีการเปรียบเทียบการใช้==มากกว่าequalsหรือมิฉะนั้นให้แน่ใจว่าไม่มีการลอยใดที่เปรียบเทียบไม่เท่ากันกับตัวเองจะถูกเก็บไว้ในตาราง มิฉะนั้นโปรแกรมที่พยายามจะนับจำนวนผลลัพธ์ที่ไม่ซ้ำกันที่สามารถสร้างจากนิพจน์เมื่อป้อนอินพุตที่หลากหลายอาจพิจารณาว่าค่า NaN ทุกรายการนั้นไม่เหมือนกัน
supercat

referes ด้านบนเพื่อไม่ให้Float float
quant_dev

พูดถึงFloatอะไร หากพยายามสร้างตารางfloatค่าที่ไม่ซ้ำกันและเปรียบเทียบกับ==กฎการเปรียบเทียบ IEEE-754 ที่น่ากลัวจะส่งผลให้ตารางนั้นเต็มไปด้วยNaNคุณค่า
supercat

floatประเภทไม่มีequalsวิธี
quant_dev

AH - ฉันไม่ได้หมายถึงequalsวิธีการเช่น แต่วิธีการที่คงที่ (ผมคิดว่าภายในFloatclass) floatซึ่งเปรียบเทียบค่าทั้งสองประเภท
supercat

9

นี่เป็นปัญหาที่ไม่เฉพาะเจาะจงกับจาวา การใช้ == เพื่อเปรียบเทียบสองทศนิยม / คู่ / หมายเลขทศนิยมใด ๆ อาจทำให้เกิดปัญหาได้เนื่องจากวิธีการจัดเก็บ โฟลว์ความแม่นยำเดียว (ตามมาตรฐาน IEEE 754) มี 32 บิตกระจายดังนี้:

1 บิต - เครื่องหมาย (0 = บวก, 1 = ลบ)
8 บิต - เลขชี้กำลัง (ตัวแทนพิเศษ (อคติ - 127)) ของ x ใน 2 ^ x)
23 บิต - Mantisa หมายเลข actuall ที่เก็บไว้

ตั๊กแตนตำข้าวเป็นสิ่งที่ทำให้เกิดปัญหา มันเหมือนกับสัญกรณ์ทางวิทยาศาสตร์เฉพาะตัวเลขในเบส 2 (ฐานสอง) ดูเหมือน 1.110011 x 2 ^ 5 หรืออะไรทำนองนั้นที่คล้ายกัน แต่ในไบนารี่ 1 แรกจะเป็น 1 เสมอ (ยกเว้นการแทนค่า 0)

ดังนั้นเพื่อประหยัดพื้นที่หน่วยความจำเล็กน้อย (ตั้งใจจะเล่น) IEEE ตัดสินใจว่าควรสันนิษฐาน 1 ตัวอย่างเช่นตั๊กแตนตำข้าวของ 1,011 จริงๆคือ 1.1011

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

Java มีปัญหาเฉพาะในภาษาที่เป็นสากลในหลายแพลตฟอร์มซึ่งแต่ละคนอาจมีรูปแบบลอยของตัวเอง สิ่งนี้ทำให้สำคัญยิ่งขึ้นในการหลีกเลี่ยง ==

วิธีที่เหมาะสมในการเปรียบเทียบสองทุ่น (ไม่ใช่เฉพาะภาษาที่คุณคิด) เพื่อความเท่าเทียมมีดังนี้:

if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
    //they are approximately equal

โดยที่ ACCEPTABLE_ERROR คือ #defined หรือค่าคงที่อื่น ๆ เท่ากับ 0.000000001 หรือความแม่นยำใด ๆ ก็ตามตามที่ Victor พูดถึงอยู่แล้ว

บางภาษามีฟังก์ชันนี้หรือค่าคงที่นี้ในตัว แต่โดยทั่วไปแล้วนี่เป็นนิสัยที่ดีที่ควรทำ


3
Java มีพฤติกรรมที่กำหนดไว้สำหรับลอย มันไม่ได้ขึ้นอยู่กับแพลตฟอร์ม
Yishai

9

ณ วันนี้วิธีที่ง่ายและรวดเร็วในการทำคือ:

if (Float.compare(sectionID, currentSectionID) == 0) {...}

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

แต่ถ้าต้องการความแม่นยำที่สูงขึ้นหรือที่กำหนดเอง

float epsilon = Float.MIN_NORMAL;  
if(Math.abs(sectionID - currentSectionID) < epsilon){...}

เป็นอีกทางเลือกในการแก้ปัญหา


1
เอกสารที่คุณเชื่อมโยงระบุ "ค่า 0 ถ้า f1 เท่ากับตัวเลข f2" ซึ่งทำให้เหมือนกับการทำ(sectionId == currentSectionId)ที่ไม่ถูกต้องสำหรับคะแนนลอย วิธี epsilon เป็นวิธีที่ดีกว่าซึ่งอยู่ในคำตอบนี้: stackoverflow.com/a/1088271/4212710
typoerrpr

8

ค่าจุดที่กำหนดไม่น่าเชื่อถือเนื่องจากข้อผิดพลาดของ roundoff

ดังนั้นจึงไม่ควรใช้เป็นค่าหลักเช่น sectionID ใช้จำนวนเต็มแทนหรือlongหากintไม่มีค่าที่เป็นไปได้เพียงพอ


2
ตกลง ระบุว่าสิ่งเหล่านี้เป็นรหัสไม่มีเหตุผลที่จะทำให้สิ่งต่าง ๆ ยุ่งยากด้วยเลขทศนิยม
Yohnny

2
หรือยาว ขึ้นอยู่กับจำนวนรหัสที่ไม่ซ้ำกันที่จะเกิดขึ้นในอนาคต int อาจมีขนาดไม่ใหญ่พอ
Wayne Hartman

ความแม่นยำเป็นสองเท่าเมื่อเทียบกับการลอย?
Arvindh Mani

1
@ArvindhMani doubles มีความแม่นยำมากขึ้น แต่พวกเขาจะยังลอยค่าจุดดังนั้นคำตอบของฉันมีความหมายที่จะรวมทั้งและfloat double
Eric Wilson

7

นอกเหนือจากคำตอบก่อนหน้านี้คุณควรทราบว่ามีพฤติกรรมแปลก ๆ ที่เกี่ยวข้องกับ-0.0fและ+0.0f(เป็น==แต่ไม่ใช่equals) และFloat.NaN(เป็นequalsแต่ไม่ใช่==) (หวังว่าฉันจะได้รับสิ่งที่ถูกต้องแล้ว - อย่าทำ!)

แก้ไข: ลองดูสิ!

import static java.lang.Float.NaN;
public class Fl {
    public static void main(String[] args) {
        System.err.println(          -0.0f   ==              0.0f);   // true
        System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
        System.err.println(            NaN   ==               NaN);   // false
        System.err.println(new Float(  NaN).equals(new Float( NaN))); // true
    }
} 

ยินดีต้อนรับสู่ IEEE / 754


หากบางสิ่งบางอย่างเป็น == ดังนั้นพวกเขาจะเหมือนกันถึงบิต พวกเขาจะไม่เท่ากับ () ได้อย่างไร? บางทีคุณอาจจะย้อนกลับไป?
mkb

@Matt NaN มีความพิเศษ Double.isNaN (double x) ใน Java ถูกนำมาใช้จริงเป็น {return x! = x; } ...
quant_dev

1
ด้วยการลอย==ไม่ได้หมายความว่าตัวเลขเป็น "เหมือนกันกับบิต" (หมายเลขเดียวกันสามารถแสดงด้วยรูปแบบบิตที่แตกต่างกันแม้ว่าหนึ่งในนั้นคือรูปแบบปกติ) เช่นกัน-0.0fและ0.0fแสดงด้วยรูปแบบบิตที่แตกต่างกัน (บิตลงชื่อแตกต่างกัน) แต่เปรียบเทียบเท่ากับ==(แต่ไม่ใช่กับequals) สมมติฐานของคุณที่==เป็นการเปรียบเทียบระดับบิตคือโดยทั่วไปแล้วพูดผิด
Pavel Minaev

6

นี่คือการอภิปรายที่ยาวมาก (แต่หวังว่าจะเป็นประโยชน์) เกี่ยวกับเรื่องนี้และประเด็นอื่น ๆ ที่คุณอาจพบ: สิ่งที่นักวิทยาศาสตร์คอมพิวเตอร์ทุกคนควรรู้เกี่ยวกับเลขทศนิยม


5

คุณสามารถใช้ Float.floatToIntBits ()

Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)

1
คุณอยู่ในเส้นทางที่ถูกต้อง floatToIntBits () เป็นวิธีที่ถูกต้อง แต่มันจะง่ายกว่าถ้าใช้ฟังก์ชั่น built-in เท่ากับ () ดูที่นี่: stackoverflow.com/a/3668105/2066079 คุณจะเห็นว่าค่าเริ่มต้นเท่ากับ () ใช้ floatToIntBits ภายใน
dberm22

1
ใช่ถ้าพวกเขาเป็นวัตถุลอย คุณสามารถใช้สมการข้างต้นเป็นพื้นฐานได้
aamadmi

4

ก่อนอื่นพวกเขาจะลอยหรือลอย? หากหนึ่งในนั้นคือ Float คุณควรใช้เมธอด equals () นอกจากนี้อาจใช้วิธี Float.compare คงที่ดีที่สุด


4

ต่อไปนี้ใช้ความแม่นยำที่ดีที่สุดโดยอัตโนมัติ:

/**
 * Compare to floats for (almost) equality. Will check whether they are
 * at most 5 ULP apart.
 */
public static boolean isFloatingEqual(float v1, float v2) {
    if (v1 == v2)
        return true;
    float absoluteDifference = Math.abs(v1 - v2);
    float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
    return absoluteDifference < 5 * maxUlp;
}

แน่นอนคุณอาจเลือกมากกว่าหรือน้อยกว่า 5 ULPs ('หน่วยในสถานที่สุดท้าย')

หากคุณเป็นห้องสมุด Apache Commons ที่Precisionชั้นจะมีcompareTo()และequals()มีทั้ง epsilon และ ULP


เมื่อเปลี่ยน float เป็น double วิธีนี้จะไม่ทำงานเหมือน isDoubleEqual (0.1 + 0.2-0.3, 0.0) == false
hychou

ดูเหมือนว่าคุณต้องการมากกว่า 10_000_000_000_000_000L เป็นปัจจัยในdoubleการครอบคลุมนี้
Michael Piefel



2

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


2

คุณจัดการกับรหัส outsourced ที่จะใช้ลอยสำหรับสิ่งที่ชื่อ sectionID และ currentSectionID? แค่สงสัย.

@ บิล K: "การแสดงเลขฐานสองของโฟลตนั้นน่ารำคาญ" งั้นเหรอ คุณจะทำให้ดีขึ้นได้อย่างไร มีตัวเลขบางอย่างที่ไม่สามารถแสดงในฐานใด ๆ ได้อย่างถูกต้องเพราะมันไม่มีวันสิ้นสุด พี่เป็นตัวอย่างที่ดี คุณสามารถประมาณได้เท่านั้น หากคุณมีทางออกที่ดีกว่าโปรดติดต่อ Intel


1

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

มีคลาส apache สำหรับเปรียบเทียบคู่: org.apache.commons.math3.util.Precision

มันมีค่าคงที่ที่น่าสนใจ: SAFE_MINและEPSILONซึ่งเป็นค่าเบี่ยงเบนที่เป็นไปได้สูงสุดของการดำเนินการทางคณิตศาสตร์อย่างง่าย

นอกจากนี้ยังมีวิธีการที่จำเป็นในการเปรียบเทียบเท่ากันหรือรอบสองเท่า (ใช้ ulps หรือส่วนเบี่ยงเบนสัมบูรณ์)


1

ในหนึ่งคำตอบบรรทัดที่ฉันสามารถพูดคุณควรใช้:

Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)

เพื่อให้คุณได้เรียนรู้เพิ่มเติมเกี่ยวกับการใช้ตัวดำเนินการที่เกี่ยวข้องอย่างถูกต้องฉันขออธิบายบางกรณีที่นี่: โดยทั่วไปมีสามวิธีในการทดสอบสตริงใน Java คุณสามารถใช้ ==, .equals () หรือ Objects.equals ()

แตกต่างกันอย่างไร == ทดสอบคุณภาพอ้างอิงในสตริงหมายถึงการค้นหาว่าวัตถุทั้งสองนั้นเหมือนกันหรือไม่ ในทางกลับกัน. Equals () ทดสอบว่าสตริงทั้งสองมีค่าเท่ากันอย่างมีเหตุผลหรือไม่ ในที่สุดการทดสอบ Objects.equals () สำหรับโมฆะใด ๆ ในสองสตริงจะกำหนดว่าจะเรียกใช้. ()

ผู้ประกอบการที่เหมาะที่จะใช้

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

อย่างไรก็ตามสิ่งที่คุณได้รับคือค่าที่ลดลงเนื่องจาก Java สร้างภาพลวงตาที่คุณกำลังเปรียบเทียบค่า แต่ในความเป็นจริงคุณไม่ได้ พิจารณาทั้งสองกรณีด้านล่าง:

กรณีที่ 1:

String a="Test";
String b="Test";
if(a==b) ===> true

กรณีที่ 2:

String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);

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

ที่นี่คุณสามารถรับรายละเอียดเพิ่มเติมได้ที่: http://fluentthemes.com/use-compare-strings-java/


-2

วิธีที่ถูกต้องก็คือ

java.lang.Float.compare(float1, float2)

7
Float.compare (float1, float2) ส่งคืน int ดังนั้นจึงไม่สามารถใช้แทน float1 == float2 ในเงื่อนไข if ยิ่งไปกว่านั้นมันไม่ได้แก้ปัญหาพื้นฐานที่คำเตือนนี้อ้างถึง - ว่าหากการลอยตัวเป็นผลลัพธ์ของการคำนวณเชิงตัวเลข float1! = float2 อาจเกิดขึ้นเนื่องจากข้อผิดพลาดในการปัดเศษ
quant_dev

1
ขวาคุณไม่สามารถคัดลอกวางคุณต้องตรวจสอบเอกสารก่อน
Eric

2
สิ่งที่คุณสามารถทำได้แทน float1 == float2 คือ Float.compare (float1, float2) == 0.
deterb

29
สิ่งนี้ไม่ได้ซื้ออะไรคุณ - คุณยังได้รับFloat.compare(1.1 + 2.2, 3.3) != 0
Pavel Minaev

-2

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

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