Phép chia int: Tại sao kết quả là 1/3 == 0?


107

Tôi đã viết mã này:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Kết quả là 0. Tại sao lại như vậy, và làm cách nào để giải quyết vấn đề này?

Câu trả lời:


147

Hai toán hạng (1 và 3) là số nguyên, do đó số học nguyên (phép chia ở đây) được sử dụng. Khai báo biến kết quả là double chỉ gây ra một chuyển đổi ngầm định xảy ra sau khi chia .

Tất nhiên, phép chia số nguyên trả về kết quả thực sự của phép chia làm tròn về 0. Do đó, kết quả của 0.333...được làm tròn xuống 0 ở đây. (Lưu ý rằng bộ xử lý không thực sự làm tròn bất kỳ, nhưng bạn vẫn có thể nghĩ về nó theo cách đó.)

Ngoài ra, lưu ý rằng nếu cả hai toán hạng (số) được cho dưới dạng số thực; 3.0 và 1.0, hoặc thậm chí chỉ là số học dấu phẩy động đầu tiên được sử dụng, mang lại cho bạn 0.333....


33
+1 và nó luôn làm tròn xuống. int i = .99999999đặt int thành 0. Cụ thể hơn, nó lấy phần nguyên và loại bỏ phần còn lại.
Byron Whitlock

37
Nó làm tròn "về phía không" là "hướng xuống" đối với các giá trị lớn hơn 0. (0,9 được làm tròn thành 0, -0,9 cũng được làm tròn đến 0.)
Adrian Smith

@Byron: Đúng vậy. Tôi không tin rằng bộ xử lý thực sự thực hiện bất kỳ phép làm tròn nào, vì phép chia được thực hiện rất khác nhau, nhưng thật tiện lợi khi nghĩ về nó theo cách đó.
Noldorin

15
Không, không làm tròn: Rút gọn
Basil Bourque

3
@BasilBourque Trong thuật ngữ java, làm trònDOWN là về 0. Làm tròn FLOORlà về phía âm vô cùng.
Andreas,

23

1/3 sử dụng phép chia số nguyên vì cả hai bên đều là số nguyên.

Bạn cần ít nhất một trong số chúng để trở thành floathoặc double.

Nếu bạn đang nhập các giá trị trong mã nguồn như câu hỏi của mình, bạn có thể làm 1.0/3; những 1.0là một đôi.

Nếu bạn nhận được các giá trị từ nơi khác, bạn có thể sử dụng (double)để biến intthành a double.

int x = ...;
int y = ...;
double value = ((double) x) / y;

10

Truyền rõ ràng nó thành một double

double g = 1.0/3.0

Điều này xảy ra vì Java sử dụng phép toán chia số nguyên cho 13vì bạn đã nhập chúng dưới dạng hằng số nguyên.


2

bạn nên sử dụng

double g=1.0/3;

hoặc là

double g=1/3.0;

Phép chia số nguyên trả về số nguyên.


2

Bởi vì bạn đang thực hiện phép chia số nguyên.

Như @Noldorin đã nói, nếu cả hai toán tử đều là số nguyên thì phép chia số nguyên sẽ được sử dụng.

Kết quả 0.33333333 không thể được biểu diễn dưới dạng số nguyên, do đó chỉ phần nguyên (0) được gán cho kết quả.

Nếu bất kỳ toán tử nào là a double/ float, thì số học dấu phẩy động sẽ diễn ra. Nhưng bạn sẽ gặp vấn đề tương tự nếu bạn làm điều đó:

int n = 1.0 / 3.0;

1

Bởi vì nó coi 1 và 3 là số nguyên, do đó làm tròn kết quả xuống 0, để nó là một số nguyên.

Để có được kết quả bạn đang tìm kiếm, hãy nói rõ ràng với java rằng các con số sẽ tăng gấp đôi như vậy:

double g = 1.0/3.0;

1

Đặt số 1 thành float và phép chia float sẽ được sử dụng

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}

1

Việc chuyển đổi trong JAVA khá đơn giản nhưng cần hiểu rõ. Như giải thích trong JLS cho các phép toán số nguyên :

Nếu một toán tử số nguyên không phải là toán tử shift có ít nhất một toán hạng kiểu dài, thì phép toán được thực hiện bằng cách sử dụng độ chính xác 64 bit và kết quả của toán tử số là kiểu dài. Nếu toán hạng khác không dài, thì đầu tiên nó được mở rộng (§5.1.5) để nhập dài bằng cách thăng hạng số (§5.6).

Và một ví dụ luôn là cách tốt nhất để dịch JLS;)

int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

Nếu không, hoạt động được thực hiện bằng cách sử dụng độ chính xác 32-bit và kết quả của toán tử số là kiểu int. Nếu một trong hai toán hạng không phải là int, thì đầu tiên nó sẽ được mở rộng để nhập int bằng cách thăng hạng số.

short + int -> int + int -> int

Một ví dụ nhỏ sử dụng Eclipse để chỉ ra rằng ngay cả việc thêm hai chữ shorts cũng không dễ dàng như vậy:

short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

Điều này sẽ yêu cầu một quá trình đúc có thể mất độ chính xác.

Điều này cũng đúng đối với các toán tử dấu phẩy động

Nếu ít nhất một trong các toán hạng của toán tử số thuộc loại double, thì phép toán được thực hiện bằng cách sử dụng số học dấu phẩy động 64 bit và kết quả của toán tử số là giá trị của loại double. Nếu toán hạng khác không phải là một kép, thì trước tiên nó được mở rộng (§5.1.5) để nhập kép bằng cách thăng hạng số (§5.6).

Vì vậy, khuyến mãi được thực hiện trên phao thành đôi.

Và sự kết hợp của cả số nguyên và giá trị động dẫn đến các giá trị động như đã nói

Nếu ít nhất một trong các toán hạng của toán tử nhị phân thuộc kiểu dấu phẩy động, thì phép toán là phép toán dấu phẩy động, ngay cả khi phép toán kia là tích phân.

Điều này đúng với các toán tử nhị phân nhưng không đúng với "Toán tử gán" như +=

Một ví dụ làm việc đơn giản là đủ để chứng minh điều này

int i = 1;
i += 1.5f;

Lý do là có một phép ép kiểu ngầm được thực hiện ở đây, điều này sẽ được thực thi như

i = (int) i + 1.5f
i = (int) 2.5f
i = 2

1

1 và 3 là các hằng số nguyên và do đó Java thực hiện phép chia số nguyên mà kết quả là 0. Nếu bạn muốn viết các hằng số kép, bạn phải viết 1.03.0.


1

Giải pháp đơn giản nhất là chỉ làm điều này

double g = ((double) 1 / 3);

Điều này làm được, vì bạn không nhập 1.0 / 3.0, cho phép bạn chuyển đổi thủ công sang kiểu dữ liệu kép vì Java cho rằng đó là phép chia Số nguyên và nó sẽ làm điều đó ngay cả khi nó có nghĩa là thu hẹp chuyển đổi. Đây là cái được gọi là toán tử ép kiểu.


1

Tôi đã làm điều này.

double g = 1.0/3.0;
System.out.printf("%gf", g);

Sử dụng .0 trong khi thực hiện các phép tính kép, nếu không Java sẽ cho rằng bạn đang sử dụng Số nguyên. Nếu một Phép tính sử dụng bất kỳ số lượng giá trị kép nào, thì kết quả đầu ra sẽ là giá trị kép. Nếu tất cả đều là Số nguyên, thì đầu ra sẽ là Số nguyên.


0

(1/3) có nghĩa là phép chia Số nguyên, đó là lý do tại sao bạn không thể nhận giá trị thập phân từ phép chia này. Để giải quyết vấn đề này, hãy sử dụng:

public static void main(String[] args) {
        double g = 1.0 / 3;
        System.out.printf("%.2f", g);
    }

0
public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Vì cả 1 và 3 đều là số nguyên nên kết quả không được làm tròn mà bị cắt bớt. Vì vậy, bạn bỏ qua phân số và chỉ lấy sỉ.

Để tránh điều này, hãy có ít nhất một trong các số 1 hoặc 3 của bạn ở dạng thập phân 1,0 và / hoặc 3,0.


0

Thử thứ này đi:

public static void main(String[] args) {
    double a = 1.0;
    double b = 3.0;
    double g = a / b;
    System.out.printf(""+ g);
}

Vui lòng không đăng các câu trả lời chỉ có mã, hãy bao gồm giải thích mã của bạn làm gì và làm thế nào (và tại sao) nó giải quyết vấn đề của câu hỏi. Cũng nên xem xét rằng đây là một câu hỏi 8 năm tuổi đã có các câu trả lời được ủng hộ và liệu câu trả lời của bạn có thêm bất kỳ giá trị nào so với các câu trả lời hiện có hay không.
Mark Rotteveel

-1

Làm "double g = 1.0 / 3.0;" thay thế.


Chỉ tò mò ... có gì sai với câu trả lời này? Tôi đã sử dụng cách tiếp cận này khi lần đầu tiên gặp sự cố. Giải thích về những gì sai với giải pháp này sẽ được đánh giá cao. Cảm ơn trước.
Ông Port St Joe

@ Mr.PortStJoe Nó không cung cấp bất kỳ chi tiết nào cho người đặt câu hỏi. Nhìn vào câu trả lời được xếp hạng cao nhất, chúng ta có thể thấy họ cũng giải thích TẠI SAO điều đó lại xảy ra. Mặc dù câu trả lời có thể đúng về mặt kỹ thuật, nhưng tính hữu dụng của nó có thể bị giới hạn.
Andrew T Finnell,

-1

Nhiều người khác đã không chỉ ra được vấn đề thực sự:

Một phép toán chỉ trên số nguyên chuyển kết quả của phép toán thành một số nguyên.

Điều này nhất thiết có nghĩa là kết quả dấu phẩy động, có thể được hiển thị dưới dạng số nguyên, sẽ bị cắt bớt (bỏ phần thập phân).

Bạn yêu cầu truyền (đánh máy / chuyển đổi kiểu) là gì?

Nó khác nhau về việc triển khai ngôn ngữ, nhưng Wikipedia có một cái nhìn khá toàn diện và nó cũng nói về sự ép buộc , đây là một phần thông tin quan trọng để trả lời câu hỏi của bạn.

http://en.wikipedia.org/wiki/Type_conversion


Câu trả lời này là thiếu sót. Không có Truyền kiểu hoặc Chuyển đổi kiểu liên quan đến tất cả các mô tả số nguyên 1/2, không phải trong ngôn ngữ đích (java). Bạn chỉ cần gọi một phép chia số nguyên, điều này sẽ dẫn đến kết quả là số nguyên. Kiểu Truyền chỉ là do chuyển đổi lên từ intthành a doubletrong quá trình gán.
YoYo

@YoYo Bạn đang nói 0,5 là một số nguyên? Tôi không nghĩ vậy, homie.
sova

1
homie này không nói gì về 0.5. Đơn giản, trong Java, 1/2là một phép chia số nguyên, dẫn đến một số nguyên bằng không. Bạn có thể gán số 0 cho số kép, nó sẽ vẫn là số 0, mặc dù là số 0.0kép.
YoYo
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.