2009-11-22 28 views
5

Tôi đã đọc cuốn sách C Primer Plus và có ví dụ nàydài đúp trong C

#include <stdio.h> 
int main(void) 
{ 
    float aboat = 32000.0; 
    double abet = 2.14e9; 
    long double dip = 5.32e-5; 

    printf("%f can be written %e\n", aboat, aboat); 
    printf("%f can be written %e\n", abet, abet); 
    printf("%f can be written %e\n", dip, dip); 

    return 0; 
} 

Sau khi tôi chạy này trên macbook của tôi Tôi đã khá sốc tại đầu ra:

32000.000000 can be written 3.200000e+04 
2140000000.000000 can be written 2.140000e+09 
2140000000.000000 can be written 2.140000e+09 

Vì vậy, Tôi nhìn quanh và phát hiện ra rằng định dạng đúng để hiển thị đôi dài là sử dụng %Lf. Tuy nhiên tôi vẫn không thể hiểu tại sao tôi có đôi giá trị abet thay vì những gì tôi nhận được khi tôi chạy nó trên Cygwin, UbuntuiDeneb đó là khoảng

-1950228512509697486020297654959439872418023994430148306244153100897726713609 
013030397828640261329800797420159101801613476402327600937901161313172717568.0 
00000 can be written 2.725000e+02 

Bất kỳ ý tưởng?

+0

Bạn có chắc bạn có mã mà chính xác? Tôi chỉ cần chạy nó trên MacBook của tôi - MacOS X 10.5.8 (GCC 4.0.1) và có kết quả Cygwin của bạn. –

+0

Có, tôi vừa kiểm tra lại. Btw Tôi đang chạy MacOS X 10.6.2 (GCC 4.2.1) – reubensammut

+0

Thực ra tôi nhận được câu trả lời vô nghĩa dự kiến ​​trên Mac. Khó hiểu. Chạy 'otool -L' trên thực thi của tôi cho tôi biết rằng nó đang chạy với /usr/lib/libSystem.B.dylib phiên bản 111.1.4; đó là thư viện cung cấp 'printf'. Nếu bạn có cùng một phiên bản của thư viện đó, bạn sẽ nhận được cùng một câu trả lời. –

Trả lời

7

Hãy thử nhìn vào quy ước gọi điện thoại trên OSX, điều này có thể giải thích được.

Tôi đoán trình biên dịch sẽ chuyển tham số đầu tiên trên ngăn xếp (hoặc trong thanh ghi FPU) và tham số double đầu tiên trong thanh ghi CPU (hoặc trên ngăn xếp). Dù bằng cách nào, chúng được truyền đi ở những nơi khác nhau. Vì vậy, khi cuộc gọi thứ ba được thực hiện, giá trị từ cuộc gọi thứ hai vẫn còn nằm xung quanh (và callee chọn nó lên). Nhưng đó chỉ là một phỏng đoán.

+0

+1 Gỡ lỗi tâm lý :) – Andomar

3

Có lẽ ABI 64 bit khác nhau theo cách mà printf tìm kiếm% f đối số ở một vị trí hoàn toàn khác với đối số% LF.

Hãy thử xem đầu ra lắp ráp (gcc -S) để xem điều này có đúng không.

+0

Tôi sẽ xem xét lắp ráp và xem liệu nó có phải mất thời gian vì tôi không thực sự tự tin trong việc lắp ráp, đặc biệt với x86-64 – reubensammut

4

printf() chức năng Các C Standard Library 's là một ví dụ về một variadic function, đó là một trong đó có thể mất các số khác nhau của các đối số. Cách mà ngôn ngữ C thực hiện điều này, hàm gọi là phải biết loại đối số nào được truyền theo thứ tự nào để có thể diễn giải chính xác chúng. Đây là lý do tại sao bạn vượt qua một chuỗi định dạng, để printf() có thể làm cho cảm giác chính xác của dữ liệu cần in.

Nếu một hàm variadic giải thích sai các đối số được truyền cho nó, tiêu chuẩn C xác định rằng hành vi là không xác định, đó là bất cứ điều gì có thể xảy ra (C89 tiêu chuẩn para 4.8.1.2). Trong trường hợp của bạn, nơi bạn chuyển các định dạng và giá trị không phù hợp đến printf(), đó là những gì đang xảy ra. Tuy nhiên, nếu bạn có một trình biên dịch tốt và mức độ cảnh báo của bạn bật lên một cái gì đó hợp lý, bạn nên được cảnh báo về điều này tại thời gian biên dịch. Ví dụ: trên Cygwin, tôi nhận được:

$ make go 
cc -g -W -Wall -Wwrite-strings -ansi -pedantic go.c -o go 
go.c: In function `main': 
go.c:10: warning: double format, long double arg (arg 2) 
go.c:10: warning: double format, long double arg (arg 3) 
go.c:10: warning: double format, long double arg (arg 2) 
go.c:10: warning: double format, long double arg (arg 3) 
$ 

Vì lý do bạn nhận được cụ thể những gì bạn đang thấy, điều này phụ thuộc vào việc triển khai cụ thể. Trong thực tế, điều có thể xảy ra là việc triển khai printf() cụ thể của bạn là diễn giải nửa đầu của số long double dưới dạng double và in giá trị tương ứng với mẫu bit cụ thể đó. Tuy nhiên, như các trạng thái tiêu chuẩn, nó có thể làm bất cứ điều gì nó thích.

+0

Cũng lưu ý rằng với variadic các hàm, các tham số 'float' luôn được quảng bá và chuyển thành' double'. Các tham số tích phân 'char' và' short' được phát triển thành 'int' (hoặc' unsigned int'). – tomlogic

+0

@tomlogic: K & R có lý do chính đáng để ép tất cả các giá trị dấu phẩy động vào cùng một loại trong các hàm variadic; ANSI C cần phải theo sau bằng cách cho phép các nguyên mẫu chỉ định một loại dấu phẩy động mà tất cả các giá trị dấu phẩy động sẽ được chuyển đổi. Nhiều trình biên dịch cho các máy tính thực hiện tính toán bằng cách sử dụng điểm nổi 80-bit làm việc xung quanh việc thiếu một tính năng như vậy bằng cách tạo ra 'dài đôi' 64 bit và không cung cấp loại 80 bit, dẫn đến sự xuống cấp của các toán học dấu phẩy động tiếp tục cho đến ngày nay. – supercat

1

Tôi đang đọc C Primer Plus, giống như bạn tôi nhận thấy điều tương tự. Xem cách tôi thay đổi các specifier định dạng cho câu lệnh printf thứ ba.

#include <stdio.h> 
#include <inttypes.h> 

int main(void){ 

    float aboat = 320000.0; 
    double abet = 2.214e9; 
    long double dip = 5.32e-5; 

    printf("%f can be written %e\n", aboat, aboat); 
    printf("%f can be written %e\n", abet, abet); 
    printf("%Lf can be written %Le\n", dip, dip); 

    return 0; 
} 

Kết quả sau khi thay đổi định dạng specifiers

320000.000000 can be written 3.200000e+05 
2214000000.000000 can be written 2.214000e+09 
0.000053 can be written 5.320000e-05 
3

Sử dụng specifier như %LF cho long double thay vì %lf hoặc %f. %LF luôn có ý nghĩa khác với %lf.

#include <stdio.h> 
int main(void) 
{ 
    float aboat = 32000.0; 
    double abet = 2.14e9; 
    long double dip = 5.32e-5L; 

    printf("%f can be written %e\n", aboat, aboat); 
    printf("%f can be written %e\n", abet, abet); 
    printf("%LF can be written %LE\n", dip, dip); 

    return 0; 
} 

Output:

The output; transcript below

32000.000000 can be written 3.200000e+04 
2140000000.000000 can be written 2.140000e+09 
0.000053 can be written 5.320000E-05 
+0

Đây là câu trả lời hay. Vui lòng đăng thêm! – wizzwizz4