Tại sao loại mặc định tăng gấp đôi?
Đó là câu hỏi được các nhà thiết kế ngôn ngữ Java yêu cầu tốt nhất. Họ là những người duy nhất biết được số tiền thực sự lý do tại sao quyết định thiết kế ngôn ngữ đó được thực hiện. Nhưng tôi cho rằng lý do là một cái gì đó dọc theo các dòng sau:
Chúng cần phân biệt giữa hai loại chữ vì chúng thực sự có nghĩa là các giá trị khác nhau ... từ góc độ toán học.
Giả sử họ đã làm "phao" mặc định cho literals, hãy xem xét ví dụ này
// (Hypothetical "java" code ...)
double d = 0.1;
double d2 = 0.1d;
Ở phía trên, các d
và d2
sẽ thực sự có giá trị khác nhau. Trong trường hợp đầu tiên, giá trị độ chính xác thấp float
được chuyển đổi thành giá trị chính xác cao hơn double
tại thời điểm gán. Nhưng bạn không thể phục hồi độ chính xác mà không có ở đó.
I posit là thiết kế ngôn ngữ nơi hai câu lệnh này đều hợp pháp và ý nghĩa khác nhau là ý tưởng BAD ... xem xét ý nghĩa thực tế của câu đầu tiên khác với ý nghĩa "tự nhiên".
Bằng cách làm việc đó theo cách mà họ đã làm được điều đó:
double d = 0.1f;
double d2 = 0.1;
đều quy phạm pháp luật, và có nghĩa là những thứ khác nhau một lần nữa. Nhưng trong tuyên bố đầu tiên, ý định của lập trình viên là rõ ràng, và câu lệnh thứ hai ý nghĩa "tự nhiên" là những gì người lập trình nhận được. Và trong trường hợp này:
float f = 0.1f;
float f2 = 0.1; // compilation error!
... trình biên dịch chọn không khớp.
Tôi đoán sử dụng phao nổi là ngoại lệ và không phải là quy tắc (sử dụng đôi thay vì) với phần cứng hiện đại như vậy tại một thời điểm nào nó sẽ làm cho tinh thần để giả định rằng người dùng có ý định 0.1f khi ông viết float f = 0.1;
Họ có thể đã làm điều đó rồi. Nhưng vấn đề là đưa ra một bộ quy tắc chuyển đổi loại hoạt động ... và đơn giản là bạn không cần một mức độ trong Java-ology để thực sự hiểu. Có 0.1
có nghĩa là những thứ khác nhau trong bối cảnh khác nhau sẽ gây nhầm lẫn.Và xem xét điều này:
void method(float f) { ... }
void method(double d) { ... }
// Which overload is called in the following?
this.method(1.0);
Thiết kế ngôn ngữ lập trình rất khó. Một sự thay đổi trong một khu vực có thể gây hậu quả cho người khác.
CẬP NHẬT để giải quyết một số điểm nêu ra bởi @supercat.
@supercat: Do quá tải trên, phương thức nào sẽ được gọi cho phương thức (16777217)? Đó có phải là lựa chọn tốt nhất không?
Tôi đã nhận xét sai ... lỗi biên dịch. Trong thực tế, câu trả lời là method(float)
.
Các JLS nói điều này:
15.12.2.5. Choosing the Most Specific Method
Nếu có nhiều hơn một phương pháp thành viên vừa tiếp cận và áp dụng đối với một phương pháp gọi , nó là cần thiết để lựa chọn một để cung cấp các mô tả cho thời gian chạy phương pháp công văn. Ngôn ngữ lập trình Java sử dụng quy tắc mà phương pháp cụ thể nhất được chọn.
...
[Các biểu tượng m1 và m2 biểu thị phương pháp được áp dụng.]
[Nếu] m2 là không chung chung, và m1 và m2 được áp dụng bởi nghiêm ngặt hoặc lỏng lẻo gọi, và nơi m1 có các kiểu tham số chính thức S1, ..., Sn và m2 có kiểu tham số chính thức T1, ..., Tn, loại Si là cụ thể hơn Ti cho đối số ei cho tất cả i (1 ≤ i ≤ n, n = k).
...
Các điều kiện trên là những trường hợp duy nhất mà theo đó một phương pháp có thể cụ thể hơn nữa.
Loại S cụ thể hơn loại T cho bất kỳ biểu thức nào nếu S <: T (§4.10).
Trong trường hợp này, chúng tôi đang so sánh method(float)
và method(double)
cả hai đều có thể áp dụng cho cuộc gọi. Kể từ float
<: double
, nó là cụ thể hơn và do đó method(float)
sẽ được chọn.
@supercat: Hành vi như vậy có thể gây ra sự cố nếu ví dụ:một biểu như int2 = (int) Math.Round(int1 * 3.5)
hoặc long2 = Math.Round(long1 * 3.5)
được thay thế bằng int1 = (int) Math.Round(int2 * 3)
hoặc long2 = Math.Round(long1 * 3)
Sự thay đổi sẽ trông vô hại, nhưng hai biểu thức đầu tiên là sửa lên đến 613566756
hoặc 2573485501354568
và sau này hai thất bại trên 5592405
[là người cuối cùng hoàn toàn không có thật trên 715827882
].
Nếu bạn đang nói về một người thực hiện thay đổi đó ... vâng.
Tuy nhiên, trình biên dịch sẽ không thực hiện thay đổi đó sau lưng của bạn. Ví dụ: int1 * 3.5
có loại double
(số int
được chuyển đổi thành double
), vì vậy bạn sẽ kết thúc cuộc gọi Math.Round(double)
.
Như một quy tắc chung, số học Java sẽ chuyển đổi hoàn toàn từ các kiểu số "nhỏ hơn" sang "lớn hơn", nhưng không phải từ "lớn hơn" thành "nhỏ hơn".
Tuy nhiên, bạn vẫn cần phải cẩn thận từ (trong ví dụ làm tròn của bạn):
sản phẩm của một số nguyên và dấu chấm động có thể không biểu diễn với đủ độ chính xác bởi vì (nói) một float
có ít bit chính xác hơn int
.
truyền kết quả của Math.round(double)
vào một loại số nguyên có thể dẫn đến chuyển đổi thành giá trị nhỏ nhất/lớn nhất của loại số nguyên.
Nhưng tất cả điều này minh họa rằng hỗ trợ số học trong ngôn ngữ lập trình là khó khăn, và không thể tránh khỏi cho một lập trình viên mới hoặc không cần thiết.
Có rất nhiều thông tin về tăng gấp đôi trên [trang này] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3), nó là một đọc tuyệt vời cho hiểu biết nổi và IEEE 754 tốt hơn. – RyPope
Tôi đã luôn luôn tự hỏi điều này thực sự. Nó xuất hiện vấn đề nằm với cách trình biên dịch phát hiện số thập phân để bắt đầu với. Nó xuất hiện để mặc định để tăng gấp đôi chữ ... Tôi đã luôn luôn có vấn đề cá nhân với việc sử dụng đôi anyway (tại sao bạn sẽ cần rất nhiều chính xác, ngay cả trong các lĩnh vực khoa học hoặc kỹ thuật?) – Singular1ty
Tôi nghĩ rằng sẽ không có nhược điểm để trình biên dịch có thể nhận ra điều đó. Vì vậy, theo như tôi có thể thấy không có lý do thực sự đằng sau nó. Một googling nhanh chóng không chỉ cho tôi một lý do hợp lệ, hoặc, nhưng tôi có thể sai. – DPM