ฉันควรใช้ double หรือ float?


90

อะไรคือข้อดีและข้อเสียของการใช้ตัวอื่นแทนใน C ++?


มีใครลองสร้างอาร์เรย์ของโฟลตและอาร์เรย์ของคู่และดูว่ามี 4 ไบต์ระหว่างสมาชิกบนโฟลตและ 8 ไบต์ระหว่างสมาชิกในคู่หรือไม่? เป็นไปได้ว่าคอมไพเลอร์ / คอมพิวเตอร์ 64 บิตอาจยังคงสงวน 8 ไบต์ต่อสมาชิกสำหรับการลอยแม้ว่าจะไม่ต้องการมากขนาดนั้นก็ตาม
user3015682

คำตอบ:


104

หากคุณต้องการที่จะรู้คำตอบที่จริงคุณควรอ่าน อะไรทุกนักวิทยาศาสตร์คอมพิวเตอร์ควรรู้เกี่ยวกับจุดลอยเลขคณิต

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

คอมไพเลอร์จำนวนมากทำคณิตศาสตร์ทศนิยมแบบขยายในโหมด "ไม่เข้มงวด" อยู่ดี (เช่นใช้ประเภทจุดลอยตัวที่กว้างกว่าที่มีอยู่ในฮาร์ดแวร์เช่น 80 บิตและ 128 บิตลอยตัว) สิ่งนี้ควรนำมาพิจารณาด้วยเช่นกัน ในทางปฏิบัติคุณแทบจะไม่เห็นความแตกต่างของความเร็วเลย - พวกมันเป็นแบบดั้งเดิมของฮาร์ดแวร์อยู่แล้ว


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

5
SSE (หรือหน่วยจุดลอยตัวในแนวดิ่ง) จะสามารถประมวลผลได้สองเท่าของจำนวนฟลอปในความแม่นยำเดียวเมื่อเทียบกับความแม่นยำสองเท่า หากคุณกำลังทำจุดลอยตัว x87 (หรือสเกลาร์ใด ๆ ) ก็คงไม่สำคัญ
Greg Rogers

1
@Greg Rogers: คอมไพเลอร์ไม่ค่อยฉลาดนักในขณะนี้ เว้นแต่คุณจะเขียนการประกอบแบบดิบก็ไม่ได้แตกต่างกันมาก และใช่สิ่งนี้อาจเปลี่ยนแปลงได้เมื่อคอมไพเลอร์มีวิวัฒนาการ
J-16 SDiZ

หมายเหตุเพิ่มเติม: หากคุณไม่รู้เลยว่าข้อมูลมีลักษณะอย่างไร (หรือไม่มีเงื่อนงำในการคำนวณทั้งหมดในลิงก์) ให้ใช้double- จะปลอดภัยกว่าในกรณีส่วนใหญ่
J-16 SDiZ

@jokoon ไม่มีอะไรง่าย ๆ ในจุดลอยตัวและพื้นที่ปัญหาความแม่นยำ / เสถียรภาพเชิงตัวเลขทั้งหมด
vonbrand

45

ถ้าคุณไม่มีเหตุผลเฉพาะบางอย่างให้ใช้ double

อาจจะน่าแปลกใจที่มันเป็นสองเท่าและไม่ใช่ลอยซึ่งเป็นประเภทจุดลอยตัว "ปกติ" ใน C (และ C ++) ฟังก์ชันทางคณิตศาสตร์มาตรฐานเช่นsinและlogใช้เป็นอาร์กิวเมนต์สองเท่าและส่งกลับค่า doubles ลิเทอรัลทศนิยมปกติเช่นเดียวกับเมื่อคุณเขียน3.14ในโปรแกรมของคุณจะมีประเภท double ไม่ลอย.

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


26

คำถามนี้ไม่สามารถตอบได้เนื่องจากไม่มีบริบทของคำถาม สิ่งที่อาจส่งผลต่อการเลือกมีดังนี้

  1. การใช้งานคอมไพเลอร์ของโฟลตคู่ผสมและแบบยาว สถานะมาตรฐาน C ++:

    จุดลอยตัวมีสามประเภท: ลอยคู่และคู่ยาว ประเภท double ให้ความแม่นยำอย่างน้อยเท่ากับ float และ type long double จะให้ความแม่นยำอย่างน้อยเท่ากับ double

    ดังนั้นทั้งสามจึงมีขนาดเท่ากันในหน่วยความจำ

  2. การมีอยู่ของ FPU ซีพียูบางตัวไม่ได้มี FPU และบางครั้งก็มีการจำลองประเภทจุดลอยตัวและบางครั้งก็ไม่รองรับประเภทจุดลอยตัว

  3. สถาปัตยกรรม FPU FPU ของ IA32 คือ 80 บิตภายใน - 32 บิตและ 64 บิตลอยได้ขยายเป็น 80 บิตเมื่อโหลดและลดลงในร้านค้า นอกจากนี้ยังมี SIMD ที่สามารถลอย 32 บิตสี่ตัวหรือ 64 บิตสองตัวในแบบคู่ขนาน การใช้ SIMD ไม่ได้กำหนดไว้ในมาตรฐานดังนั้นจึงต้องใช้คอมไพเลอร์ที่ทำการวิเคราะห์ที่ซับซ้อนมากขึ้นเพื่อตรวจสอบว่าสามารถใช้ SIMD ได้หรือไม่หรือต้องใช้ฟังก์ชันพิเศษ (ไลบรารีหรือภายใน) ผลลัพธ์ของรูปแบบภายใน 80 บิตคือคุณจะได้ผลลัพธ์ที่แตกต่างกันเล็กน้อยขึ้นอยู่กับความถี่ในการบันทึกข้อมูลลงใน RAM (ทำให้สูญเสียความแม่นยำ) ด้วยเหตุนี้คอมไพเลอร์จึงไม่ปรับแต่งโค้ดทศนิยมให้เหมาะสมโดยเฉพาะ

  4. แบนด์วิดท์หน่วยความจำ หากคู่ต้องการพื้นที่เก็บข้อมูลมากกว่าโฟลตการอ่านข้อมูลจะใช้เวลานานขึ้น นั่นเป็นคำตอบที่ไร้เดียงสา ใน IA32 สมัยใหม่ทุกอย่างขึ้นอยู่กับว่าข้อมูลมาจากไหน หากอยู่ในแคช L1 การโหลดจะน้อยมากหากข้อมูลมาจากบรรทัดแคชเดียว หากครอบคลุมมากกว่าหนึ่งบรรทัดแคชจะมีค่าใช้จ่ายเล็กน้อย หากมาจาก L2 จะใช้เวลานานกว่านั้นหากอยู่ใน RAM ก็จะยังคงอยู่อีกต่อไปและสุดท้ายถ้าอยู่ในดิสก์ก็เป็นเวลาที่ดี ดังนั้นการเลือก float หรือ double จึงมีความสำคัญน้อยกว่าวิธีการใช้ข้อมูล หากคุณต้องการคำนวณขนาดเล็กสำหรับข้อมูลตามลำดับจำนวนมากควรใช้ประเภทข้อมูลขนาดเล็ก การคำนวณจำนวนมากในชุดข้อมูลขนาดเล็กจะช่วยให้คุณใช้ประเภทข้อมูลที่ใหญ่ขึ้นและมีผลอย่างมาก ถ้าคุณ' การเข้าถึงข้อมูลแบบสุ่มมากดังนั้นการเลือกขนาดข้อมูลจึงไม่สำคัญ - ข้อมูลถูกโหลดในเพจ / บรรทัดแคช ดังนั้นแม้ว่าคุณจะต้องการแค่ไบต์จาก RAM แต่คุณก็สามารถถ่ายโอนได้ 32 ไบต์ (ซึ่งขึ้นอยู่กับสถาปัตยกรรมของระบบ) เหนือสิ่งอื่นใด CPU / FPU อาจเป็น super-scalar (aka pipelined) ดังนั้นแม้ว่าการโหลดอาจใช้เวลาหลายรอบ CPU / FPU อาจยุ่งอยู่กับการทำอย่างอื่น (เช่นการคูณ) ที่ซ่อนเวลาในการโหลดไว้ที่ระดับหนึ่ง

  5. มาตรฐานไม่ได้บังคับใช้รูปแบบเฉพาะสำหรับค่าทศนิยม

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


16

Double มีความแม่นยำมากขึ้น แต่ถูกเข้ารหัสเป็น 8 ไบต์ float มีขนาดเพียง 4 ไบต์ดังนั้นจึงมีพื้นที่น้อยลงและมีความแม่นยำน้อยลง

คุณควรระวังให้มากหากคุณมี double and float ในแอปพลิเคชันของคุณ ฉันมีข้อบกพร่องเนื่องจากในอดีต ส่วนหนึ่งของรหัสใช้ float ในขณะที่ส่วนที่เหลือของรหัสใช้ double การคัดลอกสองครั้งเพื่อลอยแล้วลอยเป็นสองเท่าอาจทำให้เกิดข้อผิดพลาดที่แม่นยำซึ่งอาจส่งผลกระทบอย่างมาก ในกรณีของฉันมันเป็นโรงงานเคมี ... หวังว่ามันจะไม่ส่งผลกระทบอย่างมาก :)

คิดว่าเป็นเพราะบั๊กแบบนี้ที่จรวด Ariane 6 ระเบิดเมื่อไม่กี่ปีก่อน !!!

คิดอย่างรอบคอบเกี่ยวกับประเภทที่จะใช้สำหรับตัวแปร


3
โปรดทราบว่าไม่รับประกัน 4/8 ไบต์สำหรับ float / double ซึ่งจะขึ้นอยู่กับแพลตฟอร์ม มันอาจจะเป็นประเภทเดียวกันด้วยซ้ำ ...
sleske

2
Ariane 5รหัสพยายามแปลง 64 บิตลอยจุดมีค่าสูงกว่า 32,767 ลง 16 บิตลงนามจำนวนเต็ม สิ่งนี้ทำให้เกิดข้อยกเว้นล้นซึ่งทำให้จรวดเริ่มต้นลำดับการทำลายตัวเอง รหัสดังกล่าวเป็นรหัสที่นำมาใช้ซ้ำจากจรวดรุ่นเก่าที่มีขนาดเล็กกว่า
cmwt

5

ตัวฉันมักจะเพิ่มขึ้นเป็นสองเท่าตลอดเวลาจนกระทั่งฉันเห็นปัญหาคอขวด จากนั้นฉันจะพิจารณาย้ายไปลอยหรือเพิ่มประสิทธิภาพส่วนอื่น ๆ


4

ขึ้นอยู่กับวิธีการที่คอมไพเลอร์ดำเนินการสองครั้ง ถูกกฎหมายสำหรับ double และ float เป็นประเภทเดียวกัน (และอยู่ในบางระบบ)

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

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


3

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


2

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



1

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

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


0

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


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