Tại sao phao vẫn là một phần của ngôn ngữ Java khi thay thế chủ yếu là đôi?


84

Ở mọi nơi tôi đã xem, nó nói rằng doublenó vượt trội hơn floathầu hết mọi cách. floatđã bị lỗi thời bởi doubleJava, vậy tại sao nó vẫn được sử dụng?

Tôi lập trình rất nhiều với Libgdx và họ buộc bạn phải sử dụng float(deltaTime, v.v.), nhưng đối với tôi, doublenó chỉ dễ làm việc hơn về mặt lưu trữ và bộ nhớ.

Tôi cũng đọc Khi nào bạn sử dụng float và khi nào bạn sử dụng double , nhưng nếu floatthực sự chỉ tốt cho các số có nhiều chữ số sau dấu thập phân, thì tại sao chúng ta không thể sử dụng một trong nhiều biến thể của double?

Có bất kỳ lý do nào khiến mọi người khăng khăng sử dụng phao mặc dù nó không thực sự có bất kỳ lợi thế nào nữa? Có phải chỉ là quá nhiều công việc để thay đổi tất cả?



58
Làm thế nào trên thế giới bạn đã suy luận "float thực sự chỉ tốt cho những con số có nhiều chữ số sau dấu thập phân" từ các câu trả lời cho câu hỏi đó?! Họ nói ngược lại !
Ordous

20
@Eames Lưu ý cách nó nói "số", không phải "chữ số". Phao tồi tệ hơn khi bạn cần độ chính xác hoặc phạm vi, chúng sẽ tốt hơn khi bạn cần rất nhiều dữ liệu không chính xác. Đó là những gì những câu trả lời nói.
Thông thường

29
Tại sao chúng ta có byteshortintkhi có long?
dùng253751

15
Một câu hỏi phù hợp hơn nhiều là "tại sao bạn lại xóa một từ khóa và kiểu dữ liệu nguyên thủy khỏi một ngôn ngữ với hàng thập kỷ mã sẽ bị hỏng mà không có lý do"?
sara

Câu trả lời:


169

LibGDX là một khung chủ yếu được sử dụng để phát triển trò chơi.

Trong phát triển trò chơi, bạn thường phải thực hiện rất nhiều cuộc khủng hoảng số trong thời gian thực và bất kỳ hiệu suất nào bạn có thể nhận được vấn đề. Đó là lý do tại sao các nhà phát triển trò chơi thường sử dụng float bất cứ khi nào độ chính xác của float là đủ tốt.

Kích thước của các thanh ghi FPU trong CPU không phải là điều duy nhất bạn cần xem xét trong trường hợp này. Trong thực tế, hầu hết các cuộc khủng hoảng số lượng lớn trong phát triển trò chơi được thực hiện bởi GPU và GPU thường được tối ưu hóa cho phao, không phải gấp đôi .

Và sau đó cũng có:

  • băng thông bus bộ nhớ (tốc độ bạn có thể chuyển dữ liệu giữa RAM, CPU và GPU)
  • Bộ nhớ cache CPU (làm cho trước đó ít cần thiết hơn)
  • RAM
  • VRAM

đó là tất cả các tài nguyên quý giá mà bạn nhận được gấp đôi khi bạn sử dụng float 32 bit thay vì 64 bit đôi.


2
Cảm ơn bạn! Điều này thực sự hữu ích vì bạn đã đi sâu vào việc sử dụng bộ nhớ đã thay đổi và tại sao
Eames

7
Ngoài ra, đối với các hoạt động SIMD, giá trị 32 bit có thể có thông lượng gấp đôi. Như câu trả lời của 8bittree chỉ ra, GPU có hình phạt hiệu năng thậm chí còn lớn hơn với độ chính xác gấp đôi.
Paul A. Clayton

5
Nhiều đường ống đồ họa thậm chí còn hỗ trợ nửa phao 16 bit để tăng hiệu suất khi độ chính xác là đủ.
Adi Shavit

22
@phresnel Tất cả đều như vậy. Bạn phải di chuyển vị trí, cập nhật dữ liệu và những gì không. Và đây là phần đơn giản . Sau đó, bạn phải kết xuất (= đọc, xoay, chia tỷ lệ và dịch) kết cấu, khoảng cách, chuyển nó sang định dạng màn hình ... Có rất nhiều việc phải làm.
13:30

8
@phresnel với tư cách là một cựu Giám đốc điều hành của một doanh nghiệp phát triển trò chơi, tôi đảm bảo với bạn rằng hầu hết mọi trò chơi đều có rất nhiều khủng hoảng. Lưu ý rằng nó thường được chứa trong các thư viện và được trừu tượng hóa 100% từ kỹ sư, tôi sẽ hy vọng họ hiểu và tôn trọng rằng tất cả những gì khủng hoảng đang diễn ra. Ma thuật nghịch đảo căn bậc hai, có ai không?
corsiKa

57

Phao sử dụng một nửa bộ nhớ gấp đôi.

Chúng có thể có độ chính xác thấp hơn gấp đôi, nhưng nhiều ứng dụng không yêu cầu độ chính xác. Chúng có phạm vi lớn hơn bất kỳ định dạng điểm cố định có kích thước tương tự. Do đó, chúng lấp đầy một ngách cần nhiều dãy số nhưng không cần độ chính xác cao và việc sử dụng bộ nhớ là quan trọng. Tôi đã sử dụng chúng cho các hệ thống mạng thần kinh lớn trong quá khứ, ví dụ.

Di chuyển ra ngoài Java, chúng cũng được sử dụng rộng rãi trong đồ họa 3D, vì nhiều GPU sử dụng chúng làm định dạng chính - bên ngoài các thiết bị NVIDIA Tesla / AMD FirePro rất đắt tiền, điểm nổi chính xác kép rất chậm trên GPU.


8
Nói về mạng thần kinh, CUDA hiện hỗ trợ các biến số dấu phẩy động chính xác một nửa (16 bit), thậm chí ít chính xác hơn nhưng với dấu chân bộ nhớ thấp hơn, do việc sử dụng máy gia tốc cho công việc học máy tăng lên.
JAB

Và khi bạn lập trình các GPU, bạn có xu hướng chọn số lượng bit cho cả mantissa và số mũ theo cách thủ công mỗi lần: v
Sebi

48

Khả năng tương thích ngược

Đây là số một lý do để giữ cho hành vi trong một đã tồn tại ngôn ngữ / thư viện / ISA / etc.

Hãy xem xét những gì sẽ xảy ra nếu họ lấy phao ra khỏi Java. Libgdx (và hàng ngàn thư viện và chương trình khác) sẽ không hoạt động. Sẽ mất rất nhiều nỗ lực để cập nhật mọi thứ, có thể là nhiều năm cho nhiều dự án (chỉ cần nhìn vào quá trình chuyển đổi Python 2 sang Python 3 tương thích ngược). Và không phải mọi thứ sẽ được cập nhật, một số thứ sẽ bị phá vỡ mãi mãi bởi vì những người bảo trì đã bỏ rơi họ, có lẽ sớm hơn họ sẽ phải mất nhiều công sức hơn họ muốn cập nhật hoặc vì không thể hoàn thành những gì phần mềm của họ được cho là làm.

Hiệu suất

Nhân đôi 64 bit chiếm gấp đôi bộ nhớ và hầu như luôn xử lý chậm hơn so với phao 32 bit (ngoại lệ rất hiếm khi sử dụng khả năng nổi 32 bit nên hiếm khi hoặc không sử dụng, mà không có nỗ lực nào được thực hiện để tối ưu hóa cho chúng Trừ khi bạn đang phát triển phần cứng chuyên dụng, bạn sẽ không trải nghiệm điều này trong tương lai gần.)

Đặc biệt có liên quan đến bạn, Libgdx là một thư viện trò chơi. Các trò chơi có xu hướng nhạy cảm về hiệu năng hơn hầu hết các phần mềm. Và các card đồ họa chơi game (ví dụ AMD Radeon và NVIDIA Geforce, không phải FirePro hay Quadro) có xu hướng có hiệu suất điểm nổi 64 bit rất yếu. Biếu không của Anandtech, đây là cách hiệu suất chính xác gấp đôi so với hiệu suất chính xác đơn trên một số thẻ chơi game hàng đầu của AMDNVIDIA (tính đến đầu năm 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

Lưu ý rằng dòng R9 Fury và GTX 900 mới hơn dòng R9 200 và GTX 700, do đó hiệu suất tương đối cho điểm nổi 64 bit đang giảm. Quay trở lại đủ xa và bạn sẽ tìm thấy GTX 580, có tỷ lệ 1/8 như loạt R9 200.

1/32 hiệu suất là một hình phạt khá lớn phải trả nếu bạn bị hạn chế về thời gian và không kiếm được nhiều tiền bằng cách sử dụng gấp đôi lớn hơn.


1
lưu ý rằng hiệu suất cho điểm nổi 64 bit đang giảm so với hiệu suất 32 bit do các hướng dẫn 32 bit được tối ưu hóa ngày càng cao, không phải vì hiệu suất 64 bit thực tế đang giảm. nó cũng phụ thuộc vào điểm chuẩn thực tế được sử dụng; Tôi tự hỏi liệu thâm hụt hiệu suất 32 bit được tô sáng trong các điểm chuẩn này có phải do vấn đề về băng thông bộ nhớ cũng như tốc độ tính toán thực tế hay không
sig_seg_v

Nếu bạn sẽ nói về hiệu suất DP trong card đồ họa, bạn chắc chắn nên đề cập đến Titan / Titan Black. Cả hai tính năng mod cho phép thẻ đạt hiệu suất 1/3, với chi phí hiệu suất chính xác duy nhất.
SGR

@sig_seg_v Chắc chắn có ít nhất một số trường hợp hiệu suất 64 bit giảm hoàn toàn, không chỉ tương đối. Xem các kết quả này để biết điểm chuẩn Folding @ Home chính xác gấp đôi, trong đó GTX 780 Ti đánh bại cả GTX 1080 (thẻ tỷ lệ 1/32 khác) và 980 Ti, và về phía AMD, 7970 (thẻ tỷ lệ 1/4) , cũng như R9 290 và R9 290X đều đánh bại loạt R9 Fury. So sánh với phiên bản chính xác duy nhất của điểm chuẩn , trong đó tất cả các thẻ mới hơn đều vượt trội so với phiên bản trước.
8bittree

36

Hoạt động nguyên tử

Ngoài những gì người khác đã nói, một nhược điểm của Java double(và long) là các bài tập cho các kiểu nguyên thủy 64 bit không được đảm bảo là nguyên tử . Từ Đặc tả ngôn ngữ Java, Phiên bản Java SE 8 , trang 660 (đã nhấn mạnh):

17.7 Điều trị phi nguyên tử của doublelong

Đối với các mục đích của mô hình bộ nhớ ngôn ngữ lập trình Java, một lần ghi vào một giá trị không biến động longhoặc doublegiá trị được coi là hai lần ghi riêng biệt: một cho mỗi nửa 32 bit. Điều này có thể dẫn đến tình huống một luồng nhìn thấy 32 bit đầu tiên của giá trị 64 bit từ một lần ghi và 32 bit thứ hai từ một lần ghi khác.

Kinh quá.

Để tránh điều này, bạn phải khai báo biến 64 bit vớivolatile từ khóa hoặc sử dụng một số hình thức đồng bộ hóa khác xung quanh các bài tập.


2
Bạn không cần phải đồng bộ hóa truy cập đồng thời vào ints và thả nổi mọi cách để ngăn chặn các cập nhật bị mất và làm cho chúng biến động để ngăn chặn bộ nhớ đệm quá mức? Có phải tôi đã sai khi nghĩ rằng điều duy nhất mà nguyên tử int / float ngăn cản là chúng không bao giờ có thể chứa các giá trị "hỗn hợp" mà chúng không được phép giữ?
Traubenfuchs

3
@Traubenfuchs Đó là, thực sự những gì được đảm bảo ở đó. Thuật ngữ tôi đã nghe được sử dụng cho nó là "xé", và tôi nghĩ rằng nó nắm bắt được hiệu ứng khá độc đáo. Mô hình ngôn ngữ lập trình Java đảm bảo rằng các giá trị 32 bit, khi đọc, sẽ có một giá trị được ghi cho chúng tại một số điểm. Đó là một đảm bảo có giá trị đáng ngạc nhiên.
Cort Ammon

Điểm này về tính nguyên tử là siêu quan trọng. Wow, tôi đã quên mất sự thật quan trọng này. Phản trực giác như chúng ta có thể có xu hướng nghĩ về nguyên thủy như là nguyên tử. Nhưng không phải nguyên tử trong trường hợp này.
Basil Bourque

3

Có vẻ như các câu trả lời khác đã bỏ lỡ một điểm quan trọng: Kiến trúc SIMD có thể xử lý ít / nhiều dữ liệu hơn tùy thuộc vào việc chúng hoạt động trên doublehay floatcấu trúc (ví dụ: tám giá trị nổi tại một thời điểm hoặc bốn giá trị kép tại một thời điểm).

Tóm tắt cân nhắc hiệu suất

  • float có thể nhanh hơn trên một số CPU nhất định (ví dụ: một số thiết bị di động nhất định).
  • float sử dụng ít bộ nhớ hơn nên trong các tập dữ liệu khổng lồ, nó có thể làm giảm đáng kể tổng bộ nhớ cần thiết (đĩa cứng / RAM) và băng thông tiêu thụ.
  • float có thể khiến CPU tiêu thụ ít năng lượng hơn (tôi không thể tìm thấy tài liệu tham khảo, nhưng nếu không thì ít nhất có vẻ hợp lý) cho các tính toán chính xác đơn so với tính toán chính xác kép.
  • float tiêu thụ ít băng thông hơn và trong một số ứng dụng quan trọng.
  • Kiến trúc SIMD có thể xử lý gấp đôi số lượng dữ liệu vì thông thường.
  • float sử dụng bằng một nửa bộ nhớ cache so với gấp đôi.

Tóm tắt cân nhắc chính xác

  • Trong nhiều ứng dụng floatlà đủ
  • double có độ chính xác cao hơn nhiều

Cân nhắc tương thích

  • Nếu dữ liệu của bạn phải được gửi tới GPU (ví dụ: đối với trò chơi video sử dụng OpenGL hoặc bất kỳ API kết xuất nào khác), định dạng dấu phẩy động nhanh hơn đáng kể so với double(đó là do các nhà sản xuất GPU cố gắng tăng số lượng lõi đồ họa và do đó, họ cố gắng tiết kiệm càng nhiều mạch càng tốt trong mỗi lõi, vì vậy tối ưu hóa cho floatphép tạo ra GPU có nhiều lõi bên trong hơn)
  • GPU cũ và một số thiết bị di động không thể chấp nhận doublelà định dạng bên trong (đối với hoạt động kết xuất 3D)

Mẹo chung

  • Trên các bộ xử lý máy tính để bàn hiện đại (và có thể là một số lượng lớn bộ xử lý di động) về cơ bản, bạn có thể giả sử sử dụng doublecác biến tạm thời trên ngăn xếp để có độ chính xác cao hơn miễn phí (độ chính xác cao hơn mà không bị phạt hiệu năng).
  • Không bao giờ sử dụng độ chính xác cao hơn mức bạn cần (bạn có thể không biết mình thực sự cần bao nhiêu độ chính xác).
  • Đôi khi bạn chỉ bị ép bởi phạm vi giá trị (một số giá trị sẽ là vô hạn nếu bạn đang sử dụng float, nhưng có thể là giá trị giới hạn nếu bạn đang sử dụng double)
  • Chỉ sử dụng floathoặc chỉ doublegiúp rất nhiều cho trình biên dịch tới SIMD-ify các hướng dẫn.

Xem ý kiến ​​dưới đây từ PeterCordes để hiểu thêm.


1
doubletạm thời chỉ miễn phí trên x86 với x87 FPU, không phải với SSE2. Auto-vectorizing một vòng lặp với doubletemporaries nghĩa giải nén floatđể double, trong đó có một hướng dẫn thêm, và bạn xử lý một nửa như nhiều yếu tố mỗi vector. Nếu không tự động vector hóa, việc chuyển đổi thường có thể xảy ra khi đang tải hoặc lưu trữ, nhưng điều đó có nghĩa là các hướng dẫn bổ sung khi bạn trộn phao và nhân đôi biểu thức.
Peter Cordes

1
Trên các CPU x86 hiện đại, div và sqrt có tốc độ nổi nhanh hơn gấp đôi, nhưng những thứ khác có cùng tốc độ (không tính vấn đề độ rộng vectơ SIMD, hoặc tất nhiên là băng thông bộ nhớ / bộ nhớ cache).
Peter Cordes

@PeterCordes cảm ơn vì đã mở rộng một số điểm. Tôi không nhận thức được sự chênh lệch giữa div và sqrt
GameDeveloper

0

Ngoài các lý do khác đã được đề cập:

Nếu bạn có dữ liệu đo, có thể là áp suất, dòng chảy, dòng điện, điện áp hoặc bất cứ điều gì, điều này thường được thực hiện với phần cứng có ADC.

Một ADC thường có 10 hoặc 12 bit, các bit 14 hoặc 16 bit hiếm hơn. Nhưng hãy sử dụng 16 bit một - nếu đo quanh thang đo đầy đủ, bạn có độ chính xác là 1/65535. Điều đó có nghĩa là một sự thay đổi từ 65534/65535 thành 65535/65535 chỉ là bước này - 1/65535. Đó là khoảng 1,5E-05. Độ chính xác của phao là khoảng 1E-07, vì vậy tốt hơn rất nhiều. Điều đó có nghĩa là bạn không mất gì khi sử dụng floatđể lưu trữ những dữ liệu này.

Nếu bạn tính toán quá mức với phao, bạn thực hiện kém hơn so với doublesđộ chính xác, nhưng bạn thường không cần độ chính xác đó, vì bạn thường không quan tâm nếu bạn chỉ đo điện áp 2 V hoặc 2.00002 V. Tương tự , nếu bạn chuyển đổi điện áp này thành áp suất, bạn không quan tâm nếu bạn có 3 bar hoặc 3.00003 bar.

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.