เหตุใดจึงยังคงเป็นส่วนหนึ่งของภาษา Java เมื่อแนะนำเป็นสองเท่าแทน?


84

ในทุกที่ที่ฉันดูมันบอกว่าdoubleมันยอดเยี่ยมกว่าfloatในเกือบทุกด้าน floatถูกทำให้ล้าสมัยโดยdoubleใน Java ดังนั้นทำไมมันยังคงใช้?

ฉันเขียนโปรแกรมเป็นจำนวนมากกับ Libgdx และพวกเขาบังคับให้คุณใช้float(deltaTime, ฯลฯ ) แต่ดูเหมือนว่าสำหรับฉันแล้วdoubleมันจะง่ายกว่าที่จะทำงานด้วยในแง่ของการจัดเก็บและหน่วยความจำ

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

มีเหตุผลว่าทำไมคนอื่นยืนยันที่จะใช้ลอยแม้ว่ามันจะไม่ได้ประโยชน์อีกต่อไป? มันเป็นงานมากเกินไปที่จะเปลี่ยนแปลงทั้งหมดหรือไม่



58
ในโลกนี้คุณอนุมานว่า "การลอยเป็นสิ่งที่ดีสำหรับตัวเลขที่มีตัวเลขจำนวนมากหลังจากจุดทศนิยม" จากคำตอบของคำถามนั้น! พวกเขากล่าวว่าตรงข้ามโดยตรง !
Ordous

20
@Eames โปรดสังเกตว่ามันบอกว่า "numbers" ไม่ใช่ "หลัก" โฟลตนั้นแย่กว่าเมื่อคุณต้องการความแม่นยำหรือพิสัยพวกมันจะดีกว่าเมื่อคุณต้องการข้อมูลจำนวนมากและไม่แม่นยำ นั่นคือสิ่งที่คำตอบเหล่านั้นพูด
59

29
ทำไมเรามีbyteและshortและintเมื่อมีlong?
Immibis

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

คำตอบ:


169

LibGDX เป็นเฟรมเวิร์กส่วนใหญ่ที่ใช้สำหรับการพัฒนาเกม

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

ขนาดของการลงทะเบียน FPU ใน CPU ไม่ใช่สิ่งเดียวที่คุณต้องพิจารณาในกรณีนี้ ในความเป็นจริงมากที่สุดของจำนวนหนักกระทืบในการพัฒนาเกมจะกระทำโดย GPU และGPUs มักจะเหมาะสำหรับการลอยไม่คู่ผสม

แล้วก็มี:

  • แบนด์วิดธ์บัสหน่วยความจำ (ความเร็วในการพลั่วข้อมูลระหว่าง RAM, CPU และ GPU)
  • CPU cache (ซึ่งทำให้จำเป็นน้อยกว่าก่อนหน้านี้)
  • แกะ
  • VRAM

ซึ่งเป็นทรัพยากรที่มีค่าทั้งหมดซึ่งคุณได้รับมากเป็นสองเท่าเมื่อคุณใช้ 32 บิตลอยแทน 64 บิตสองเท่า


2
ขอขอบคุณ! สิ่งนี้ช่วยได้จริงๆเพราะคุณได้เจาะลึกถึงสิ่งที่การใช้งานหน่วยความจำเปลี่ยนไปและทำไม
เมส

7
นอกจากนี้สำหรับการดำเนินการ SIMD ค่า 32 บิตสามารถมีปริมาณงานได้สองเท่า เมื่อคำตอบของ 8bittreeชี้ให้เห็นว่า GPU มีบทลงโทษที่มีประสิทธิภาพมากกว่าและมีความแม่นยำสองเท่า
Paul A. Clayton

5
ไปป์ไลน์กราฟิกจำนวนมากรองรับการทำงานแบบ 16 บิตครึ่งลอยเพื่อเพิ่มประสิทธิภาพโดยที่ความแม่นยำนั้นเพียงพอ
Adi Shavit

22
@phresnel ทั้งหมดคือ คุณต้องย้ายตำแหน่งอัปเดตข้อมูลและสิ่งที่ไม่ และนี่คือส่วนที่เรียบง่าย จากนั้นคุณต้องแสดงผล (= อ่าน, หมุน, ปรับขนาดและแปล) พื้นผิว, ระยะทาง, นำไปเป็นรูปแบบหน้าจอ ... มีอะไรให้ทำมากมาย
Sebb

8
@phresnel ในฐานะอดีต VP Operations ขององค์กรพัฒนาเกมฉันรับรองกับคุณว่าเกือบทุกเกมจะมีการกระทืบจำนวนมาก โปรดทราบว่ามันมักจะมีอยู่ในห้องสมุดและแยกออกจากวิศวกร 100% ฉันหวังว่าพวกเขาจะเข้าใจและเคารพในสิ่งที่เกิดขึ้น สแควร์รูทเวทย์มนตร์ใคร ๆ
corsiKa

57

โฟลตใช้หน่วยความจำครึ่งหนึ่งเท่าตัว

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

การย้ายออกนอก Java พวกเขายังใช้กันอย่างแพร่หลายในกราฟิก 3D เพราะ GPU จำนวนมากใช้เป็นรูปแบบหลักของพวกเขา - นอกเหนือจากอุปกรณ์ NVIDIA Tesla / AMD FirePro แพงมากจุดลอยตัวที่มีความแม่นยำสองเท่านั้นช้ามากสำหรับ GPU


8
การพูดของโครงข่ายประสาทเทียมปัจจุบัน CUDA ได้รองรับตัวแปรทศนิยมที่มีความแม่นยำครึ่งหนึ่ง (16 บิต) แม้จะแม่นยำน้อยกว่า แต่มีรอยเท้าหน่วยความจำที่ต่ำกว่าเนื่องจากมีการใช้งานเครื่องเร่งความเร็วสำหรับการเรียนรู้เครื่องจักร
JAB

และเมื่อคุณตั้งโปรแกรม FPGA คุณมักจะเลือกจำนวนบิตสำหรับทั้ง mantissa และ exponent ด้วยตนเองทุกครั้ง: v
Sebi

48

ความเข้ากันได้ย้อนหลัง

นี้เป็นจำนวนหนึ่งเหตุผลสำหรับการรักษาพฤติกรรมในที่มีอยู่แล้วภาษา / ห้องสมุด / ISA / ฯลฯ

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

ประสิทธิภาพ

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

Libgdx เป็นห้องสมุดเกมโดยเฉพาะที่เกี่ยวข้องกับคุณ เกมมีแนวโน้มที่จะมีความไวต่อประสิทธิภาพมากกว่าซอฟต์แวร์ส่วนใหญ่ และกราฟิกการ์ดเกม (เช่น AMD Radeon และ NVIDIA Geforce ไม่ใช่ FirePro หรือ Quadro) มีแนวโน้มที่จะมีประสิทธิภาพการทำงาน 64 บิตที่ต่ำมาก ได้รับความอนุเคราะห์จาก Anandtech นี่คือวิธีที่ประสิทธิภาพความแม่นยำสองเท่าเมื่อเทียบกับประสิทธิภาพความแม่นยำเดียวของการ์ดเกมชั้นนำของAMDและNVIDIAบางรุ่น (ตั้งแต่ต้นปี 2016)

AMD
Card    R9 Fury X      R9 Fury       R9 290X    R9 290
FP64    1/16           1/16          1/8        1/8

NVIDIA
Card    GTX Titan X    GTX 980 Ti    GTX 980    GTX 780 Ti
FP64    1/32           1/32          1/32       1/24

โปรดทราบว่า R9 Fury และ GTX 900 series ใหม่กว่า R9 200 และ GTX 700 series ดังนั้นประสิทธิภาพสัมพัทธ์ของ 64 บิตจะลดลง ย้อนกลับไปไกลพอแล้วคุณจะพบ GTX 580 ซึ่งมีอัตราส่วน 1/8 เช่น R9 200 series

1/32 ของการแสดงนั้นเป็นบทลงโทษที่ดีมากหากคุณมีเวลา จำกัด และไม่ได้รับผลตอบแทนมากนักจากการใช้ double ที่ใหญ่ขึ้น


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

หากคุณกำลังจะพูดถึงประสิทธิภาพของ DP ในกราฟิกการ์ดคุณควรพูดถึง Titan / Titan Black อย่างแน่นอน คุณสมบัติการปรับเปลี่ยนทั้งสองอย่างที่ช่วยให้การ์ดมีประสิทธิภาพถึง 1/3 ในราคาเดียวกับประสิทธิภาพที่แม่นยำ
SGR

@sig_seg_v มีอย่างน้อยในบางกรณีที่ประสิทธิภาพ 64 บิตลดลงอย่างแน่นอนไม่เพียง แต่ค่อนข้าง ดูผลลัพธ์เหล่านี้สำหรับการวัดค่าความแม่นยำสองเท่าแบบพับ @ Home ที่ GTX 780 Ti ชนะทั้ง GTX 1080 (การ์ดอัตราส่วน 1/32 อื่น) และ 980 Ti และด้าน 7970 (การ์ดอัตราส่วน 1/4) เช่นเดียวกับ R9 290 และ R9 290X ล้วนชนะซีรี่ส์ R9 Fury เปรียบเทียบกับรุ่นที่มีความแม่นยำมาตรฐานเดียวซึ่งการ์ดรุ่นใหม่ทั้งหมดมีประสิทธิภาพสูงกว่ารุ่นก่อน ๆ
8bittree

36

ปฏิบัติการปรมาณู

นอกเหนือไปจากสิ่งที่คนอื่นได้แล้วกล่าวว่าข้อเสีย Java ที่เฉพาะเจาะจงของdouble(และlong) คือการที่ได้รับมอบหมายถึง 64 บิตชนิดดั้งเดิมไม่ได้รับประกันว่าจะอะตอม จากข้อกำหนดภาษา Java, Java SE 8 Edition , หน้า 660 (เน้นการเน้น):

17.7 การรักษาแบบไม่ปรมาณูdoubleและlong

สำหรับวัตถุประสงค์ของโมเดลหน่วยความจำภาษาการเขียนโปรแกรม Java การเขียนหนึ่งครั้งไปยังที่ไม่ลบเลือนlongหรือdoubleค่าจะถูกพิจารณาเป็นสองการเขียนแยก: หนึ่งถึงแต่ละครึ่ง 32- บิต สิ่งนี้อาจส่งผลให้เกิดสถานการณ์ที่เธรดเห็น 32 บิตแรกของค่า 64- บิตจากการเขียนหนึ่งครั้งและ 32 บิตที่สองจากการเขียนอื่น

yuck

เพื่อหลีกเลี่ยงปัญหานี้คุณต้องประกาศตัวแปร 64 บิตด้วยvolatileคำสำคัญหรือใช้รูปแบบการซิงโครไนซ์อื่น ๆรอบการมอบหมาย


2
คุณไม่จำเป็นต้องซิงโครไนซ์การเข้าถึงพร้อมกันเพื่อ ints และลอย ๆ ต่อไปเพื่อป้องกันการปรับปรุงที่สูญหาย ฉันผิดที่คิดว่าสิ่งเดียวที่ปรมาณู int / float ป้องกันคือพวกเขาไม่สามารถมีค่า "ผสม" ที่พวกเขาไม่ควรถือ?
Traubenfuchs

3
@ Traubenfuchs นั่นคือสิ่งที่รับประกันแน่นอน คำที่ฉันได้ยินมาใช้สำหรับมันคือ "ฉีกขาด" และฉันคิดว่ามันสามารถจับเอฟเฟกต์ได้ค่อนข้างดี รูปแบบภาษาการเขียนโปรแกรม Java รับประกันว่าค่า 32 บิตเมื่ออ่านจะมีค่าที่ถูกเขียนถึงพวกเขาในบางจุด นั่นคือการรับประกันที่มีค่าอย่างน่าประหลาดใจ
Cort Ammon

ประเด็นเกี่ยวกับอะตอมมิกนี้สำคัญมาก ว้าวฉันลืมความจริงสำคัญนี้ไปแล้ว ต่อต้านอย่างง่าย ๆ เพราะเราอาจคิดว่าสิ่งดั้งเดิมเป็นอะตอมโดยธรรมชาติ แต่ไม่ใช่อะตอมในกรณีนี้
Basil Bourque

3

ดูเหมือนว่าคำตอบอื่น ๆ พลาดจุดสำคัญอย่างหนึ่ง: สถาปัตยกรรมSIMDสามารถประมวลผลข้อมูลน้อยลง / มากขึ้นขึ้นอยู่กับว่าพวกเขาทำงานdoubleหรือfloatstructs (ตัวอย่างเช่นค่าทศนิยมแปดครั้งต่อครั้งหรือสี่ค่าสองครั้ง)

สรุปข้อควรพิจารณาเกี่ยวกับประสิทธิภาพ

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

สรุปการพิจารณาความแม่นยำ

  • ในแอพพลิเคชั่นfloatก็เพียงพอแล้ว
  • double มีความแม่นยำมากขึ้นอยู่ดี

ข้อควรพิจารณาเกี่ยวกับความเข้ากันได้

  • หากข้อมูลของคุณต้องถูกส่งไปยัง GPU (เช่นสำหรับวิดีโอเกมที่ใช้OpenGLหรือ API การแสดงผลอื่น ๆ ) รูปแบบจุดลอยตัวจะเร็วกว่ามากdouble(นั่นเป็นเพราะผู้ผลิต GPU พยายามเพิ่มจำนวนแกนกราฟิกและ ดังนั้นพวกเขาจึงพยายามประหยัดวงจรให้มากที่สุดเท่าที่จะเป็นไปได้ในแต่ละแกนดังนั้นการปรับให้เหมาะสมเพื่อfloatให้สามารถสร้าง GPU ที่มีแกนภายในมากขึ้น)
  • GPU เก่าและอุปกรณ์มือถือบางรุ่นไม่สามารถยอมรับdoubleว่าเป็นรูปแบบภายใน (สำหรับการแสดงผล 3D)

เคล็ดลับทั่วไป

  • บนโปรเซสเซอร์เดสก์ท็อปที่ทันสมัย ​​(และอาจเป็นจำนวนที่ดีของโปรเซสเซอร์มือถือ) โดยทั่วไปคุณสามารถสันนิษฐานได้ว่าการใช้doubleตัวแปรชั่วคราวบนสแต็กจะให้ความแม่นยำเป็นพิเศษสำหรับฟรี
  • อย่าใช้ความแม่นยำมากกว่าที่คุณต้องการ (คุณอาจไม่รู้ความแม่นยำที่คุณต้องการจริงๆ)
  • บางครั้งคุณถูกบังคับโดยช่วงของค่า (บางค่าจะไม่มีที่สิ้นสุดหากคุณกำลังใช้floatแต่อาจมีค่า จำกัด หากคุณกำลังใช้double)
  • การใช้เพียงอย่างเดียวเท่านั้นfloatหรือdoubleอย่างยิ่งช่วยให้คอมไพเลอร์เพื่อ SIMD-ify คำแนะนำ

ดูความคิดเห็นด้านล่างจาก PeterCordes สำหรับข้อมูลเชิงลึกเพิ่มเติม


1
doubleชั่วขณะนั้นฟรีใน x86 ด้วย x87 FPU ไม่ใช่ SSE2 Auto-vectorizing ห่วงกับdoubleชั่วคราวหมายถึงการเปิดออกfloatไปdoubleซึ่งจะมีการเรียนการสอนพิเศษและคุณดำเนินการครึ่งหลายองค์ประกอบตามเวกเตอร์ หากไม่มีการแปลงเป็นเวกเตอร์อัตโนมัติการแปลงสามารถเกิดขึ้นได้ทันทีระหว่างการโหลดหรือเก็บ แต่มันหมายถึงคำแนะนำพิเศษเมื่อคุณผสมลอยและเพิ่มเป็นสองเท่าในนิพจน์
Peter Cordes

1
สำหรับซีพียู x86 รุ่นใหม่ div และ sqrt นั้นเร็วกว่าแบบลอยตัวกว่าเท่าตัว แต่สิ่งอื่น ๆ นั้นมีความเร็วเท่ากัน (ไม่นับปัญหาความกว้างเวกเตอร์ SIMD หรือหน่วยความจำแบนด์วิธ / แคชรอยเท้าแน่นอน)
Peter Cordes

@ PeterCordes ขอบคุณสำหรับการขยายบางจุด ฉันไม่ทราบถึงความแตกต่างระหว่าง div และ sqrt
GameDeveloper

0

นอกเหนือจากเหตุผลอื่น ๆ ที่กล่าวถึง:

หากคุณมีข้อมูลการวัดไม่ว่าจะเป็นแรงกดดันกระแสกระแสไฟฟ้าแรงดันหรืออะไรก็ตามมักทำกับฮาร์ดแวร์ที่มี ADC

โดยทั่วไปแล้ว ADC จะมี 10 หรือ 12 บิตโดยที่บิต 14 หรือ 16 บิตนั้นหายากกว่า แต่ลองติดที่ 16 บิตหนึ่ง - ถ้าวัดรอบสเกลเต็มคุณมีความแม่นยำที่ 1/65535 นั่นหมายถึงการเปลี่ยนจาก 65534/65535 เป็น 65535/65535 เป็นเพียงขั้นตอนนี้ - 1/65535 นั่นคือประมาณ 1.5E-05 ความแม่นยำของการลอยอยู่ที่ประมาณ 1E-07 ดังนั้นจึงดีกว่ามาก นั่นหมายความว่าคุณจะไม่สูญเสียสิ่งใดโดยใช้floatสำหรับการจัดเก็บข้อมูลเหล่านี้

หากคุณทำการคำนวณด้วยโฟลตมากเกินไปคุณทำได้แย่กว่าdoublesในแง่ของความแม่นยำ แต่บ่อยครั้งที่คุณไม่ต้องการความถูกต้องเนื่องจากคุณมักไม่สนใจว่าคุณเพิ่งวัดแรงดันไฟฟ้า 2 V หรือ 2.00002 V. ในทำนองเดียวกัน ถ้าคุณแปลงแรงดันไฟฟ้านี้เป็นความดันคุณไม่สนใจว่าคุณมี 3 บาร์หรือ 3.00003 บาร์

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