Khi nào bạn sử dụng float và khi nào bạn sử dụng gấp đôi


194

Thường xuyên trong kinh nghiệm lập trình của tôi, tôi cần đưa ra quyết định liệu tôi nên sử dụng float hay double cho các số thực của mình. Đôi khi tôi đi phao, đôi khi tôi đi gấp đôi, nhưng thực sự điều này cảm thấy chủ quan hơn. Nếu tôi phải đối mặt để bảo vệ quyết định của mình, có lẽ tôi sẽ không đưa ra lý do hợp lý.

Khi nào bạn sử dụng float và khi nào bạn sử dụng double? Bạn có luôn sử dụng gấp đôi, chỉ khi có sự hạn chế về bộ nhớ thì bạn mới nổi? Hoặc bạn sử dụng luôn nổi trừ khi yêu cầu chính xác yêu cầu bạn sử dụng gấp đôi? Có một số khác biệt đáng kể về độ phức tạp tính toán của các số liệu cơ bản giữa float và double? Những ưu và nhược điểm của việc sử dụng float hay double là gì? Và bạn thậm chí đã sử dụng lâu dài gấp đôi?


28
Trong nhiều trường hợp, bạn không muốn sử dụng, mà là loại dấu phẩy động hoặc điểm cố định thập phân. Các loại dấu phẩy động nhị phân không thể biểu thị hầu hết các số thập phân chính xác.
CodeInChaos

3
Liên quan đến nguyên nhân gây ra lỗi làm tròn điểm nổi? . @CodesInChaos câu trả lời của tôi ở đó gợi ý các tài nguyên để giúp bạn đưa ra quyết định đó, không có giải pháp một kích cỡ phù hợp cho tất cả .
Đánh dấu gian hàng

Câu trả lời rất hay được tìm thấy tại: Stack Overflow
Haris

5
Chính xác những gì bạn có nghĩa là "số thập phân". Nếu bạn cần biểu diễn các giá trị như 0,01 chính xác (giả sử là tiền), thì dấu phẩy động (nhị phân) không phải là câu trả lời. Nếu bạn chỉ có nghĩa là số không nguyên, thì dấu phẩy động có thể ổn - nhưng "số thập phân" không phải là từ tốt nhất để mô tả những gì bạn cần.
Keith Thompson

1
Bạn không phải lúc nào cũng có lựa chọn. Ví dụ, trên nền tảng Arduino, cả hai và float tương đương với float. Bạn cần tìm một thư viện bổ trợ để xử lý các nhân đôi thực sự.
kiwiron

Câu trả lời:


187

Lựa chọn mặc định cho loại dấu phẩy động phải là double. Đây cũng là loại mà bạn nhận được với literals dấu chấm động mà không có một hậu tố hoặc (trong C) các chức năng tiêu chuẩn hoạt động trên số dấu chấm động (ví dụ exp, sinvv).

float chỉ nên được sử dụng nếu bạn cần thao tác trên nhiều số dấu phẩy động (suy nghĩ theo thứ tự hàng nghìn trở lên) và phân tích thuật toán đã chỉ ra rằng phạm vi giảm và độ chính xác không gây ra vấn đề.

long doublecó thể được sử dụng nếu bạn cần nhiều phạm vi hoặc độ chính xác hơn doublevà nếu nó cung cấp điều này trên nền tảng mục tiêu của bạn.

Tóm lại, floatlong doublenên được dành riêng để sử dụng bởi các chuyên gia, với doubleviệc sử dụng "mỗi ngày".


10
Tôi có thể sẽ không xem xét float cho một vài nghìn giá trị trừ khi có vấn đề về hiệu năng liên quan đến bộ nhớ đệm và truyền dữ liệu dấu phẩy động. Thường có một chi phí đáng kể để thực hiện phân tích để chỉ ra rằng float là đủ chính xác.
Patricia Shanahan

4
Là một phụ lục, nếu bạn cần khả năng tương thích với các hệ thống khác, có thể thuận lợi khi sử dụng cùng loại dữ liệu.
zzzzBov

15
Tôi sẽ sử dụng số float cho hàng triệu số chứ không phải 1000. Ngoài ra, một số GPU làm tốt hơn với phao, trong trường hợp chuyên dụng đó sử dụng phao. Khác, như bạn nói, sử dụng gấp đôi.
dùng949300

4
@PatriciaShanahan - 'vấn đề về hiệu suất liên quan đến ..' Một ví dụ điển hình là nếu bạn dự định sử dụng SSE2 hoặc các hướng dẫn vectơ tương tự, bạn có thể thực hiện 4 ops / vector trong float (so với 2 trên mỗi đôi) có thể cải thiện tốc độ đáng kể ( một nửa số ops và một nửa số lượng dữ liệu để đọc và ghi). Điều này có thể làm giảm đáng kể ngưỡng mà việc sử dụng phao trở nên hấp dẫn và đáng để xử lý các vấn đề về số.
greggo

12
Tôi tán thành câu trả lời này bằng một lời khuyên bổ sung: Khi một người đang hoạt động với các giá trị RGB để hiển thị, có thể chấp nhận sử dụng float(và đôi khi chính xác một nửa) vì cả mắt người, màn hình hoặc hệ màu đều có nhiều bit chính xác . Lời khuyên này có thể áp dụng để nói OpenGL, vv Lời khuyên bổ sung này không áp dụng cho hình ảnh y tế, có yêu cầu chính xác nghiêm ngặt hơn.
rwong

42

Rất hiếm khi sử dụng float thay vì tăng gấp đôi mã nhắm vào các máy tính hiện đại. Độ chính xác bổ sung làm giảm (nhưng không loại trừ) cơ hội làm tròn lỗi hoặc sự thiếu chính xác khác gây ra vấn đề.

Những lý do chính tôi có thể nghĩ đến để sử dụng float là:

  1. Bạn đang lưu trữ các mảng số lớn và cần giảm mức tiêu thụ bộ nhớ của chương trình.
  2. Bạn đang nhắm mục tiêu một hệ thống không hỗ trợ điểm nổi chính xác kép. Cho đến gần đây, nhiều card đồ họa chỉ hỗ trợ các điểm nổi chính xác duy nhất. Tôi chắc chắn có rất nhiều bộ xử lý nhúng và công suất thấp cũng có hỗ trợ điểm nổi hạn chế.
  3. Bạn đang nhắm mục tiêu phần cứng trong đó độ chính xác đơn nhanh hơn độ chính xác kép và ứng dụng của bạn sử dụng rất nhiều số học dấu phẩy động. Trên các CPU Intel hiện đại, tôi tin rằng tất cả các phép tính dấu phẩy động được thực hiện với độ chính xác gấp đôi, vì vậy bạn không đạt được bất cứ điều gì ở đây.
  4. Bạn đang thực hiện tối ưu hóa ở mức độ thấp, ví dụ như sử dụng các hướng dẫn CPU đặc biệt hoạt động trên nhiều số cùng một lúc.

Vì vậy, về cơ bản, gấp đôi là cách để đi trừ khi bạn có giới hạn phần cứng hoặc trừ khi phân tích cho thấy rằng việc lưu trữ số chính xác gấp đôi đang góp phần đáng kể vào việc sử dụng bộ nhớ.


2
"Máy tính hiện đại" có nghĩa là bộ xử lý Intel x86. Một số máy mà Người xưa sử dụng cung cấp độ chính xác hoàn toàn phù hợp với loại phao cơ bản. (CDC 6600 sử dụng một từ 60-bit, 48 bit bình thường floating-point mantissa, 12 bit của số mũ đó là gần như những gì x86 cung cấp cho bạn với độ chính xác kép..)
John R. Strohm

@ John.R.Strohm: đã đồng ý, nhưng trình biên dịch C không tồn tại trên CDC6600. Đó là Fortran IV ...
Basile Starynkevitch

Theo "máy tính hiện đại", ý tôi là bất kỳ bộ xử lý nào được chế tạo trong một hoặc hai thập kỷ qua, hoặc thực sự, vì tiêu chuẩn điểm nổi của IEEE đã được triển khai rộng rãi. Tôi hoàn toàn biết rằng các kiến ​​trúc không phải là x86 tồn tại và có ý nghĩ đó với câu trả lời của tôi - Tôi đã đề cập đến GPU và bộ xử lý nhúng, thường không phải là x86.
dùng611910

Điều đó chỉ đơn giản là không đúng sự thật. SSE2 có thể thao tác 4 phao hoặc 2 nhân đôi trong một thao tác, AVX có thể thao tác 8 phao hoặc 4 nhân đôi, AVX-512 có thể thao tác 16 phao hoặc 8 nhân đôi. Đối với bất kỳ loại tính toán hiệu năng cao nào, toán học trên phao nên được coi là gấp đôi tốc độ của các hoạt động tương tự tăng gấp đôi trên x86.
Larry Gritz

1
Và thậm chí còn tệ hơn thế, vì bạn có thể chứa gấp đôi số phao trong bộ đệm của bộ xử lý gấp đôi và độ trễ bộ nhớ có thể là nút cổ chai chính trong nhiều chương trình. Giữ cho toàn bộ bộ phao làm việc ấm trong bộ nhớ cache có thể thực sự là một trật tự cường độ nhanh hơn so với sử dụng gấp đôi và khiến chúng tràn vào RAM.
Larry Gritz

10

Sử dụng doublecho tất cả các tính toán và biến tạm thời của bạn. Sử dụng floatkhi bạn cần duy trì một dãy số - float[](nếu độ chính xác là đủ) và bạn đang xử lý hơn hàng chục ngàn floatsố.

Nhiều / hầu hết các hàm toán học hoặc toán tử chuyển đổi / trả về doublevà bạn không muốn chuyển các số trở lại floatcho bất kỳ bước trung gian nào.

Ví dụ: Nếu bạn có đầu vào 100.000 số từ một tệp hoặc một luồng và cần sắp xếp chúng, hãy đặt các số đó vào một float[].


5

Một số nền tảng (ARM Cortex-M2, Cortex-M4, v.v.) không hỗ trợ gấp đôi (Nó luôn có thể được kiểm tra trong hướng dẫn tham khảo cho bộ xử lý của bạn. Nếu không có cảnh báo biên dịch hoặc lỗi, điều đó không có nghĩa là mã là tối ưu. gấp đôi có thể được mô phỏng.). Đó là lý do tại sao bạn có thể cần phải bám vào int hoặc float .

Nếu đó không phải là trường hợp, tôi sẽ sử dụng gấp đôi .

Bạn có thể kiểm tra bài báo nổi tiếng của D. Goldberg ("Điều mà mọi nhà khoa học máy tính nên biết về số học dấu phẩy động"). Bạn nên suy nghĩ kỹ trước khi sử dụng số học dấu phẩy động. Có một cơ hội khá lớn mà họ không cần thiết trong tình huống cụ thể của bạn.

http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf


3
Câu hỏi này đã được trả lời khá tốt một năm trước ... nhưng trong mọi trường hợp, tôi muốn nói rằng bất cứ khi nào bạn sử dụng gấp đôi trên nền tảng với khả năng tăng tốc độ chính xác gấp đôi, bạn nên sử dụng nó trên bất kỳ phương tiện nào khác, ngay cả khi điều đó có nghĩa là hãy để trình biên dịch mô phỏng nó thay vì tận dụng lợi thế của FPU chỉ bằng dấu phẩy động (lưu ý rằng không phải yêu cầu của FPU trên tất cả các nền tảng, trên thực tế, kiến ​​trúc Cortex-M4 định nghĩa chúng là một tính năng tùy chọn [có phải là lỗi chính tả không?] ).
Selali Adobor

Chìa khóa của logic đó là, trong khi đó, người ta thực sự nên sử dụng số học dấu phẩy động và nhiều "quirks", chắc chắn không có sự hỗ trợ của FPU để nhân đôi có nghĩa là chỉ cần sử dụng gấp đôi thay vì phao. Phao thường rất nhanh hơn gấp đôi và chiếm ít bộ nhớ hơn (tính năng của FPU khác nhau). Khối lượng sử dụng loại trừ điểm này là tối ưu hóa sớm. Thực tế là nhân đôi rõ ràng là quá mức cần thiết cho rất nhiều ứng dụng (thậm chí là hầu hết). Các yếu tố trên trang này có thực sự cần phải có vị trí và kích thước tương đối của chúng được tính đến 13 vị trí thập phân không?
Selali Adobor

2
Khi bao gồm một liên kết đến một trang hoặc tài liệu ngoài trang web, vui lòng sao chép thông tin liên quan hoặc tóm tắt từ tài liệu vào câu trả lời của bạn. Tắt liên kết trang web có xu hướng biến mất theo thời gian.
Adam Zuckerman

3

Đối với các vấn đề trong thế giới thực, ngưỡng lấy mẫu dữ liệu của bạn rất quan trọng khi trả lời câu hỏi này. Tương tự, sàn tiếng ồn cũng rất quan trọng. Nếu một trong hai bị vượt quá bởi lựa chọn loại dữ liệu của bạn, sẽ không có lợi ích nào đến từ việc tăng độ chính xác.

Hầu hết các bộ lấy mẫu trong thế giới thực được giới hạn ở 24 s DAC. Đề xuất rằng 32 bit độ chính xác trên các tính toán trong thế giới thực phải là đủ trong đó mức độ chính xác là 24 bit.

Độ chính xác gấp đôi đi kèm với chi phí của bộ nhớ gấp 2 lần. Do đó, việc hạn chế sử dụng gấp đôi số phao có thể cắt giảm đáng kể dung lượng bộ nhớ / băng thông của các ứng dụng đang chạy.


-3

Việc lựa chọn sử dụng biến nào giữa float và double phụ thuộc vào độ chính xác của dữ liệu cần thiết. Nếu một câu trả lời là bắt buộc phải có sự khác biệt không đáng kể so với câu trả lời thực tế, số lượng vị trí thập phân được yêu cầu sẽ nhiều, do đó sẽ ra lệnh sử dụng gấp đôi. Phần mềm sẽ cắt bỏ một phần vị trí thập phân do đó làm giảm độ chính xác.


3
Câu trả lời này không thêm bất cứ điều gì mới vào câu hỏi và không nói gì về việc sử dụng thực tế.
Martijn Pieters

-5

Thông thường, tôi sử dụng floatloại khi tôi không cần nhiều độ chính xác - ví dụ: đối với tiền - đó là sai, nhưng đó là những gì tôi đã sử dụng sai.

Mặt khác, tôi sử dụng doublekhi tôi cần độ chính xác cao hơn, ví dụ cho các thuật toán toán học phức tạp.

Tiêu chuẩn C99 nói điều này:

Có ba loại điểm nổi: float, double và long double. Loại đôi cung cấp ít nhất độ chính xác như phao và loại dài gấp đôi cung cấp ít nhất độ chính xác như gấp đôi. Tập hợp các giá trị của kiểu float là tập hợp con của tập hợp các giá trị của kiểu double; tập hợp các giá trị của kiểu double là tập con của tập hợp các giá trị của kiểu double double.

Tôi chưa bao giờ thực sự sử dụng long double, nhưng tôi không sử dụng C / C ++ nhiều như vậy. Thông thường tôi sử dụng các ngôn ngữ được gõ động như Python, nơi bạn không phải quan tâm đến các loại.

Để biết thêm thông tin về Double vs Float , xem câu hỏi này tại SO .


25
Sử dụng dấu phẩy động để tính toán tiền nghiêm trọng có lẽ là một sai lầm.
Bart van Ingen Schenau

17
float chính xác là loại sai cho tiền. Bạn cần phải sử dụng độ chính xác cao nhất có thể.
ChrisF

8
@BartvanIngenSchenau Điểm nổi cho tiền thường ổn, điểm nổi nhị phân thì không. Ví dụ: .net Decimallà một loại dấu phẩy động và nó thường là một lựa chọn tốt để tính toán tiền.
CodeInChaos

13
@ChrisF Bạn không cần "độ chính xác cao" để kiếm tiền, bạn cần các giá trị chính xác.
Sean McS Something

2
@SeanMcS Something - Điểm công bằng. Tuy nhiên, phao vẫn là loại sai và được cung cấp các loại dấu phẩy động có sẵn trong hầu hết các ngôn ngữ bạn cần "độ chính xác cao" để có được "giá trị chính xác".
ChrisF
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.