Tại sao "f" lại được yêu cầu khi khai báo float?


86

Thí dụ:

float timeRemaining = 0.58f;

Tại sao lại fbắt buộc phải có ở cuối con số này?


4
Có thể, bởi vì nếu không nó sẽ được coi là double.
Uwe Keim

2
0,58 (không có hậu tố f) là một ký tự kiểu double và người ta không thể gán giá trị kép cho một float, giống như người ta không thể gán giá trị int cho một chuỗi. Tuy nhiên, bạn có thể gán giá trị float cho double vì ở đây bạn đang mở rộng (C # sẽ chuyển đổi ngầm định giá trị này cho bạn vì không có độ chính xác nào bị mất).
Dave New


@AVD Mặc dù đây là một bản sao, tiêu đề câu hỏi ở trên không mang lại liên kết của bạn trong danh sách có thể trùng lặp. Vì vậy, điều này ít nhất có thể tăng thêm giá trị về nhiều tiêu chí tìm kiếm.
Adam Houldsworth

1
@AdamHouldsworth - mọi con đường đều đi đếndouble & int .
adatapost vào

Câu trả lời:


96

Khai báo float của bạn có hai phần:

  1. Nó tuyên bố rằng biến timeRemaininglà kiểu float.
  2. Nó chỉ định giá trị 0.58cho biến này.

Vấn đề xảy ra ở phần 2.

Phía bên tay phải được tự đánh giá. Theo đặc tả C #, một số chứa dấu thập phân không có hậu tố được hiểu là a double.

Vì vậy, bây giờ chúng ta có một doublegiá trị mà chúng ta muốn gán cho một biến kiểu float. Để làm được điều này, phải có một chuyển đổi ngầm định từ doublethành float. Không có chuyển đổi như vậy, bởi vì bạn có thể (và trong trường hợp này là) mất thông tin trong chuyển đổi.

Lý do là giá trị được trình biên dịch sử dụng không thực sự là 0,58 mà là giá trị dấu phẩy động gần nhất với 0,58, là 0,57999999999999978655962351581366 ... cho doublevà chính xác là 0,579999946057796478271484375 cho float.

Nói một cách chính xác, flà không bắt buộc. Bạn có thể tránh phải sử dụng fhậu tố bằng cách chuyển giá trị thành float:

float timeRemaining = (float)0.58;

4
Hai câu hỏi, làm thế nào bạn đến số '0.579999946057796478271484375', và công việc sẽ như thế nào (float) 0.58? Trước đó bạn đã nói rằng không có chuyển đổi, vì thông tin có thể bị mất, sau đó làm thế nào đến diễn viên sẽ hoạt động?
SexyBeast

8
1. Tôi đã sử dụng máy tính Windows, sử dụng tới 34 chữ số. 2. Tôi đã nói không có chuyển đổi ngầm . Truyền là một chuyển đổi rõ ràng, vì vậy bạn đang nói rõ ràng với trình biên dịch rằng bạn muốn chuyển đổi và điều đó được phép.
Jeffrey Sax

Thật thú vị ... Tôi hy vọng bạn vẫn theo dõi bài đăng này. Tôi đã được giới thiệu với các hậu tố đã chạy vào 'm' trước đây cho webcast tôi đang xem. Làm cách nào để máy tính Windows hiển thị .58 dưới dạng giá trị float? Tôi đã thử một vài nút có vẻ như chúng sẽ buộc điều này xảy ra nhưng không ... tiếp tục nhận được 0,58. Tôi phải tôn trọng một người đàn ông biết công cụ của mình rất tốt ...
user1585204

Tôi hiểu toàn bộ điều F cho float nhưng tại sao? Khi bạn khai báo, biến được khai báo là float nên khi biên dịch nó phải biết giá trị này là float. Và khi bạn khai báo một điều giống nhau. Nó không có ý nghĩa vì bạn có float myvalue = 2,4; bây giờ khi biên dịch trình biên dịch nên đã biết float là kiểu của biến. Tui bỏ lỡ điều gì vậy? Cảm ơn!
Frank G.

@FrankG. Hai lý do: 1. Biểu thức 2.4được hiểu là một kép ở mọi nơi khác. 2. Không cho phép các chuyển đổi thu hẹp ngầm định (như từ double sang float). Nếu bạn muốn tạo một ngoại lệ cho những quy tắc này, thì bạn phải có một lý do chính đáng. Lưu 1 hành trình phím có thể không đủ tốt.
Jeffrey Sax

36

Bởi vì có một số kiểu số mà trình biên dịch có thể sử dụng để biểu diễn giá trị 0.58:float , doubledecimal. Trừ khi bạn đồng ý với việc chọn một trình biên dịch cho bạn, bạn phải xác định rõ.

Tài liệu cho doublebiết rằng nếu bạn không tự mình chỉ định loại, trình biên dịch luôn chọn doublelà loại của bất kỳ ký tự số thực nào:

Theo mặc định, một ký tự số thực ở phía bên phải của toán tử gán được coi là kép. Tuy nhiên, nếu bạn muốn một số nguyên được coi là gấp đôi, hãy sử dụng hậu tố d hoặc D.

Thêm hậu tố ftạo ra một float; hậu tố dtạo ra mộtdouble ; hậu tố mtạo ra a decimal. Tất cả những thứ này cũng hoạt động ở dạng chữ hoa.

Tuy nhiên, điều này vẫn chưa đủ để giải thích tại sao điều này không biên dịch:

float timeRemaining = 0.58;

Nửa còn thiếu của câu trả lời là việc chuyển đổi từ double 0.58đến float timeRemainingkhả năng mất thông tin, do đó trình biên dịch từ chối áp dụng nó ngầm. Nếu bạn thêm một diễn viên rõ ràng thì chuyển đổi sẽ được thực hiện; nếu bạn thêm fhậu tố thì không cần chuyển đổi. Trong cả hai trường hợp, mã sau đó sẽ được biên dịch.


nhưng làm thế nào nó có thể là một đôi hoặc một số thập phân nếu biến là một phao, tôi đang thiếu một cái gì đó
Thomas

@BlazArt Có, câu cho biết trình biên dịch thậm chí không cố gắng kiểm tra mục tiêu gán. Lý do cho điều này sẽ bị chôn vùi trong các lựa chọn thiết kế do nhóm biên dịch thực hiện. Tôi nghĩ tốt nhất là nên rõ ràng khi đối mặt với sự nhầm lẫn có thể xảy ra, hoặc quá tốn kém để thực hiện thay vì chỉ có hai quy tắc, một cho intvà một cho double.
Adam Houldsworth

@BlazArt: Vừa mới hoàn thành câu trả lời, vui lòng xem lại.
Jon

1
Liên quan đến việc mất thông tin, bạn có thể cho tôi biết quá trình cast thực sự được thực hiện như thế nào, đối với định dạng IEEE 754 mà chúng được lưu trữ? Double có độ chính xác cao hơn, vì vậy điều đó có nghĩa là đúc từ float sang double sẽ luôn hoạt động, như thế double a = 0.69f;nào?
SexyBeast

@Cupidvogel: Có, thông số kỹ thuật đảm bảo (trong §6.1.2) rằng "các chuyển đổi số ẩn khác không bao giờ mất bất kỳ thông tin nào", trong số những thông số khác bao gồm chuyển đổi floatsang double.
Jon

2

Vấn đề là .NET, để cho phép một số loại hoạt động ngầm được thực hiện liên quan đến floatdouble , cần phải chỉ định rõ ràng điều gì sẽ xảy ra trong tất cả các tình huống liên quan đến toán hạng hỗn hợp hoặc cho phép chuyển đổi ngầm giữa các loại được thực hiện trong một chỉ hướng; Microsoft đã chọn đi theo hướng dẫn đầu của Java trong việc cho phép hướng mà đôi khi ủng hộ độ chính xác, nhưng thường hy sinh tính đúng đắn và thường gây ra rắc rối.

Trong hầu hết các trường hợp, lấy doublegiá trị gần nhất với một đại lượng số cụ thể và gán nó cho a floatsẽ mang lại floatgiá trị gần nhất với cùng đại lượng đó. Có một vài trường hợp góc, chẳng hạn như giá trị 9,007,199,791,611,905; floatđại diện tốt nhất sẽ là 9,007,200,328,482,816 (giảm bởi 536,870,911), nhưng đúc doubleđại diện tốt nhất (tức là 9,007,199,791,611,904) để floattạo ra 9,007,199,254,740,992 (giảm 536,870,913). Mặc dù vậy, nói chung, việc chuyển đổi doublebiểu diễn tốt nhất của một số đại lượng thành floatsẽ mang lại biểu diễn tốt nhất có thể float, hoặc một trong hai biểu diễn về cơ bản tốt như nhau.

Lưu ý rằng hành vi mong muốn này áp dụng ngay cả ở các cực điểm; ví dụ: floatbiểu diễn tốt nhất cho đại lượng 10 ^ 308 khớp với floatbiểu diễn đạt được bằng cách chuyển đổi doublebiểu diễn tốt nhất của đại lượng đó. Tương tự như vậy, floatbiểu diễn tốt nhất của 10 ^ 309 khớp với floatbiểu diễn đạt được bằng cách chuyển đổi doublebiểu diễn tốt nhất của đại lượng đó.

Thật không may, các chuyển đổi theo hướng không yêu cầu diễn viên rõ ràng hiếm khi ở bất kỳ đâu gần chính xác. Việc chuyển đổi floatbiểu diễn tốt nhất của một giá trị thành doublesẽ hiếm khi mang lại bất kỳ điều gì đặc biệt gần với doublebiểu diễn tốt nhất của giá trị đó và trong một số trường hợp, kết quả có thể bị sai lệch hàng trăm bậc (ví dụ: chuyển đổi floatbiểu diễn tốt nhất của 10 ^ 40 thành doublesẽ mang lại một giá trị so sánh lớn hơn doubleđại diện tốt nhất của 10 ^ 300.

Than ôi, các quy tắc chuyển đổi là những gì họ đang có, vì vậy người ta phải sống với việc sử dụng các định dạng và hậu tố ngớ ngẩn khi chuyển đổi các giá trị theo hướng "an toàn" và hãy cẩn thận với các định dạng ngầm theo hướng nguy hiểm thường sẽ mang lại kết quả không có thật.

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.