Tôi nên sử dụng double hay float?


89

Ưu điểm và nhược điểm của việc sử dụng cái này thay vì cái kia trong C ++ là gì?


Có ai đã thử tạo một mảng float và một mảng đôi và xem liệu có thực sự là 4 byte giữa các thành viên trên phao và 8 byte giữa các thành viên trên đôi không? Có thể một trình biên dịch / máy tính 64 bit vẫn có thể dành 8 byte cho mỗi thành viên cho phao mặc dù chúng không cần nhiều như vậy.
user3015682

Câu trả lời:


104

Nếu bạn muốn biết câu trả lời thực sự, bạn nên đọc Điều mà mọi nhà khoa học máy tính nên biết về số học dấu chấm động .

Tóm lại, mặc dù doublecho phép biểu diễn độ chính xác cao hơn , nhưng đối với một số phép tính nhất định, nó sẽ tạo ra sai số lớn hơn . Lựa chọn "đúng" là: sử dụng càng nhiều độ chính xác càng tốt nhưng không nhiều hơnchọn đúng thuật toán .

Nhiều trình biên dịch thực hiện phép toán dấu phẩy động mở rộng ở chế độ "không nghiêm ngặt" (tức là sử dụng kiểu dấu phẩy động rộng hơn có sẵn trong phần cứng, ví dụ: 80-bit và 128-bit nổi), điều này cũng nên được tính đến. Trong thực tế, bạn khó có thể thấy bất kỳ sự khác biệt nào về tốc độ - dù sao thì chúng cũng là bản gốc của phần cứng.


10
Đúng. Với các CPU hiện đại tìm nạp trước các khối bộ nhớ lớn hơn và lớn hơn, các đơn vị xử lý số song song và kiến ​​trúc đường ống, vấn đề tốc độ thực sự không phải là một vấn đề. Nếu bạn đang xử lý số lượng lớn các con số, thì có lẽ sự khác biệt về kích thước giữa float 4 byte và kép 8 byte có thể tạo ra sự khác biệt về diện tích bộ nhớ.
lavinio

5
Well SSE (hoặc bất kỳ đơn vị dấu chấm động vertor nào) sẽ có thể xử lý gấp đôi số lần lỗi ở độ chính xác đơn so với độ chính xác kép. Nếu bạn chỉ làm x87 (hoặc bất kỳ dấu chấm động vô hướng nào) thì nó có thể không thành vấn đề.
Greg Rogers

1
@Greg Rogers: các trình biên dịch không phải là thông minh tại thời điểm này. Trừ khi bạn đang viết assembly thô, nó không có khác biệt lớn. Và có, điều này có thể thay đổi khi trình biên dịch phát triển.
J-16 SDiZ

Một lưu ý bổ sung: Nếu bạn hoàn toàn không biết dữ liệu trông như thế nào (hoặc không có manh mối nào về tất cả các phép toán trong các liên kết), chỉ cần sử dụng double- nó an toàn hơn trong hầu hết các trường hợp.
J-16 SDiZ

2
Bài báo này không ngắn.
jokoon

44

Trừ khi bạn có một số lý do cụ thể để làm khác, hãy sử dụng double.

Có lẽ đáng ngạc nhiên, nó là double chứ không phải float là kiểu dấu phẩy động "bình thường" trong C (và C ++). Các hàm toán học tiêu chuẩn như sinlog nhận gấp đôi đối số và trả về gấp đôi. Một ký tự dấu phẩy động bình thường, như khi bạn viết 3,14 trong chương trình của mình, có kiểu double. Không nổi.

Trên các máy tính hiện đại điển hình, tốc độ nhân đôi có thể nhanh như phao hoặc thậm chí nhanh hơn, vì vậy hiệu suất thường không phải là yếu tố cần xem xét, ngay cả đối với các phép tính lớn. (Và đó sẽ phải là những phép tính lớn , hoặc hiệu suất thậm chí không nằm trong tâm trí bạn. Máy tính để bàn i7 mới của tôi có thể thực hiện sáu tỷ phép nhân đôi trong một giây.)


26

Câu hỏi này là không thể trả lời vì không có ngữ cảnh cho câu hỏi. Dưới đây là một số điều có thể ảnh hưởng đến sự lựa chọn:

  1. Trình biên dịch thực hiện phao, đôi và đôi dài. Tiêu chuẩn C ++ tuyên bố:

    Có ba loại dấu chấm động: float, double, và long double. Loại double cung cấp độ chính xác ít nhất bằng float và loại long double cung cấp độ chính xác ít nhất là gấp đôi.

    Vì vậy, cả ba có thể có cùng kích thước trong bộ nhớ.

  2. Sự hiện diện của một FPU. Không phải tất cả các CPU đều có FPU và đôi khi kiểu dấu phẩy động được mô phỏng và đôi khi kiểu dấu chấm động không được hỗ trợ.

  3. Kiến trúc FPU. FPU của IA32 là 80bit bên trong - các phao 32 bit và 64 bit được mở rộng thành 80bit khi tải và giảm khi lưu trữ. Ngoài ra còn có SIMD có thể thực hiện song song bốn phao 32bit hoặc hai phao 64bit. Việc sử dụng SIMD không được xác định trong tiêu chuẩn, vì vậy nó sẽ yêu cầu một trình biên dịch thực hiện phân tích phức tạp hơn để xác định xem SIMD có thể được sử dụng hay không, hoặc yêu cầu sử dụng các chức năng đặc biệt (thư viện hoặc bản chất). Kết quả của định dạng nội bộ 80bit là bạn có thể nhận được các kết quả hơi khác nhau tùy thuộc vào tần suất dữ liệu được lưu vào RAM (do đó, làm mất độ chính xác). Vì lý do này, các trình biên dịch không tối ưu hóa mã dấu chấm động đặc biệt tốt.

  4. Băng thông bộ nhớ. Nếu một double yêu cầu nhiều bộ nhớ hơn float, thì sẽ mất nhiều thời gian hơn để đọc dữ liệu. Đó là câu trả lời ngây thơ. Trên IA32 hiện đại, tất cả phụ thuộc vào nguồn dữ liệu đến từ đâu. Nếu nó nằm trong bộ nhớ cache L1, tải không đáng kể với điều kiện dữ liệu đến từ một dòng bộ nhớ cache duy nhất. Nếu nó kéo dài nhiều hơn một dòng bộ nhớ cache thì sẽ có một chi phí nhỏ. Nếu là từ L2, thì phải mất một lúc lâu hơn, nếu ở trong RAM thì lâu hơn và cuối cùng, nếu ở trên đĩa thì đó là thời gian rất lớn. Vì vậy, sự lựa chọn float hoặc double ít quan trọng hơn cách dữ liệu được sử dụng. Nếu bạn muốn thực hiện một phép tính nhỏ trên nhiều dữ liệu tuần tự, thì nên chọn kiểu dữ liệu nhỏ. Thực hiện nhiều tính toán trên một tập dữ liệu nhỏ sẽ cho phép bạn sử dụng các kiểu dữ liệu lớn hơn với bất kỳ hiệu quả đáng kể nào. Nếu bạn' đang truy cập dữ liệu một cách rất ngẫu nhiên, khi đó việc lựa chọn kích thước dữ liệu là không quan trọng - dữ liệu được tải trong các trang / dòng bộ nhớ cache. Vì vậy, ngay cả khi bạn chỉ muốn một byte từ RAM, bạn có thể nhận được 32 byte được chuyển (điều này rất phụ thuộc vào kiến ​​trúc của hệ thống). Trên hết, CPU / FPU có thể là siêu vô hướng (hay còn gọi là pipelined). Vì vậy, mặc dù tải có thể mất vài chu kỳ, CPU / FPU có thể bận làm việc khác (ví dụ: nhân) mà ẩn thời gian tải ở một mức độ.

  5. Tiêu chuẩn không thực thi bất kỳ định dạng cụ thể nào cho các giá trị dấu chấm động.

Nếu bạn có một thông số kỹ thuật, thì điều đó sẽ hướng dẫn bạn đến sự lựa chọn tối ưu. Nếu không, bạn phải trải nghiệm xem nên sử dụng những gì.


16

Double chính xác hơn nhưng được mã hóa trên 8 byte. float chỉ có 4 byte, do đó ít chỗ hơn và kém chính xác hơn.

Bạn nên rất cẩn thận nếu bạn có double và float trong ứng dụng của mình. Tôi đã có một lỗi do đó trong quá khứ. Một phần của mã đang sử dụng float trong khi phần còn lại của mã đang sử dụng double. Sao chép double sang float và sau đó float thành double có thể gây ra lỗi độ chính xác có thể gây ảnh hưởng lớn. Trong trường hợp của tôi, đó là một nhà máy hóa chất ... hy vọng nó không gây ra hậu quả nghiêm trọng :)

Tôi nghĩ rằng chính vì loại lỗi này mà tên lửa Ariane 6 đã nổ cách đây vài năm !!!

Hãy suy nghĩ cẩn thận về loại được sử dụng cho một biến


3
Lưu ý rằng 4/8 byte cho float / double thậm chí không được đảm bảo, nó sẽ phụ thuộc vào nền tảng. Nó thậm chí có thể là cùng một loại ...
sleske

2
Ariane 5 đã cố gắng chuyển đổi một dấu phẩy động 64 bit, có giá trị lớn hơn 32,767, thành một số nguyên có dấu 16 bit. Điều này tạo ra một ngoại lệ tràn khiến tên lửa bắt đầu chuỗi tự hủy của nó. Mã được đề cập, là mã được sử dụng lại từ một tên lửa cũ hơn, nhỏ hơn.
cmwt

5

Tôi phân tích nhân đôi mọi lúc cho đến khi tôi thấy một số nút thắt cổ chai. Sau đó, tôi cân nhắc chuyển sang thả nổi hoặc tối ưu hóa một số phần khác


4

Điều này phụ thuộc vào cách trình biên dịch thực hiện double. Nó hợp pháp khi double và float là cùng một loại (và nó có trên một số hệ thống).

Tuy nhiên, nếu chúng thực sự khác nhau, vấn đề chính là độ chính xác. Một đôi có độ chính xác cao hơn nhiều do sự khác biệt về kích thước. Nếu các số bạn đang sử dụng thường vượt quá giá trị của dấu phẩy, thì hãy sử dụng dấu kép.

Một số người khác đã đề cập đến vấn đề hiệu suất. Đó chính xác là thứ cuối cùng trong danh sách cân nhắc của tôi. Tính đúng đắn nên được bạn cân nhắc số 1.



2

Tôi nghĩ bất kể sự khác biệt (như mọi người đã chỉ ra, float chiếm ít dung lượng hơn và nói chung là nhanh hơn) ... có ai từng gặp vấn đề về hiệu suất khi sử dụng double không? Tôi nói rằng hãy sử dụng double ... và nếu sau này bạn quyết định "wow, điều này thực sự chậm" ... hãy tìm điểm nghẽn hiệu suất của bạn (có thể không phải là thực tế bạn đã sử dụng double). VẬY, nếu nó vẫn còn quá chậm đối với bạn, hãy xem nơi bạn có thể hy sinh một số độ chính xác và sử dụng float.



1

Nó phụ thuộc nhiều vào CPU, sự cân bằng rõ ràng nhất là giữa độ chính xác và bộ nhớ. Với hàng GB RAM, bộ nhớ không phải là vấn đề lớn, vì vậy nói chung tốt hơn là sử dụng doubles.

Đối với hiệu suất, nó phụ thuộc nhiều vào CPU. floats thường sẽ có hiệu suất tốt hơn doubles trên máy 32 bit. Trên 64 bit, doubles đôi khi nhanh hơn, vì nó (thường) là kích thước gốc. Tuy nhiên, điều quan trọng hơn việc bạn lựa chọn kiểu dữ liệu là liệu bạn có thể tận dụng các hướng dẫn SIMD trên bộ xử lý của mình hay không.


0

double có độ chính xác cao hơn, trong khi float chiếm ít bộ nhớ hơn và nhanh hơn. Nói chung, bạn nên sử dụng float trừ khi bạn gặp trường hợp nó không đủ chính xác.


5
Trên các máy tính hiện đại điển hình, double cũng nhanh như float.
Thomas Padron-McCarthy
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.