2012-12-20 19 views
13

Lưu ý: Tôi đã hỏi nhầm về số static_cast ban đầu; đây là lý do tại sao câu trả lời hàng đầu đề cập đến số static_cast lúc đầu.Có an toàn để reinterpret_cast một số nguyên để nổi không?

Tôi có một số tệp nhị phân có ít giá trị float cuối cùng. Tôi muốn đọc chúng một cách độc lập với máy. Các thường trình hoán đổi byte của tôi (từ SDL) hoạt động trên các kiểu số nguyên không dấu.

Có an toàn để chỉ đơn giản là cast giữa ints và phao nổi?

float read_float() { 
    // Read in 4 bytes. 
    Uint32 val; 
    fread(&val, 4, 1, fp); 
    // Swap the bytes to little-endian if necessary. 
    val = SDL_SwapLE32(val); 
    // Return as a float 
    return reinterpret_cast<float &>(val); //XXX Is this safe? 
} 

Tôi muốn phần mềm này càng đơn giản càng tốt.

+2

Bạn chỉ có thể 'reinterpret_cast' thành con trỏ hoặc các loại tham chiếu; mã bạn đã đăng sẽ không biên dịch. – Praetorian

+1

Bạn có thể có nghĩa là một cái gì đó như float result = reintrepret_cast (val); –

+0

Rất tiếc. Tôi thực sự nên đã cố gắng để biên dịch đầu tiên:/ – QuasarDonkey

Trả lời

24

Vâng, static_cast là "an toàn" và nó đã xác định hành vi, nhưng điều này có lẽ không phải là những gì bạn cần. Chuyển đổi một giá trị tích phân thành kiểu float sẽ đơn giản cố gắng biểu diễn cùng một giá trị tích phân trong kiểu dấu phẩy động mục tiêu. I E. 5 của loại int sẽ biến thành 5.0 loại float (giả sử nó chính là biểu tượng chính xác).

Điều bạn có vẻ đang làm là xây dựng đại diện đối tượng của giá trị float trong một bộ nhớ được khai báo là biến Uint32. Để tạo kết quả float giá trị bạn cần phải reinterpret bộ nhớ đó. Điều này sẽ được thực hiện bằng reinterpret_cast

assert(sizeof(float) == sizeof val); 
return reinterpret_cast<float &>(val); 

hoặc, nếu bạn thích, một phiên bản con trỏ của điều tương tự

assert(sizeof(float) == sizeof val); 
return *reinterpret_cast<float *>(&val); 

Mặc dù loại này loại-punning không được bảo đảm để làm việc trong một trình biên dịch rằng sau ngữ nghĩa nghiêm ngặt. Một cách tiếp cận khác là thực hiện việc này

float f; 

assert(sizeof f == sizeof val); 
memcpy(&f, &val, sizeof f); 

return f; 

Hoặc bạn có thể sử dụng công đoàn nổi tiếng để thực hiện giải thích lại bộ nhớ. Một số trình biên dịch có ngữ nghĩa nghiêm ngặt đặt trước cách tiếp cận dựa trên công đoàn như một phương thức được hỗ trợ chính thức của loại-punning

assert(sizeof(float) == sizeof(Uint32)); 

union { 
    Uint32 val; 
    float f; 
} u = { val }; 

return u.f; 
+0

Xin lỗi, bạn là chính xác (tôi có nghĩa là reinterpret_cast, không static_cast). Tôi đã cập nhật câu hỏi để phản ánh điều này. – QuasarDonkey

+0

Cảm ơn. Tôi không biết thuật ngữ "kiểu-punning". Nó đã bật lên một số thông tin hữu ích. Dựa trên những gì tôi đã đọc, tôi nghĩ rằng tôi sẽ đi với * liên minh * lừa, nó có vẻ được hỗ trợ tốt. – QuasarDonkey

+0

Điều gì xảy ra nếu chúng ta bỏ qua * đầu tiên và sau đó thả nổi * để vượt qua phép nhân của 4? Nó có an toàn không? –

2

Tóm lại, không chính xác. Bạn đang đúc một số nguyên cho một phao, và nó sẽ được giải thích bởi trình biên dịch dưới dạng số nguyên tại thời điểm đó. Giải pháp công đoàn trình bày các công trình trên.

Một cách khác để làm cùng một loại điều như sự hợp nhất là sẽ được sử dụng này:

return *reinterpret_cast<float*>(&val); 

Nó cũng không kém phần an toàn/không an toàn như các giải pháp công đoàn trên, và tôi chắc chắn sẽ khuyên bạn nên một khẳng định để đảm bảo phao có cùng kích thước với int. Tôi cũng sẽ cảnh báo rằng có các định dạng điểm nổi không tương thích IEEE-754 hoặc IEEE-854 (hai tiêu chuẩn này có cùng định dạng cho số float, tôi không hoàn toàn chắc chắn sự khác biệt chi tiết là gì, thành thật). Vì vậy, nếu bạn có một máy tính sử dụng một định dạng dấu chấm động khác, nó sẽ rơi xuống. Tôi không chắc liệu có cách nào để kiểm tra điều đó, ngoài việc có thể có một tập hợp các byte được lưu trữ ở đâu đó, cùng với các giá trị mong đợi trong float, sau đó chuyển đổi các giá trị và xem nó có xuất hiện đúng không.

+0

Tôi đã đọc bài viết wiki về loại-punning bây giờ. Nó nói: "Trên nhiều nền tảng phổ biến, việc sử dụng con trỏ xảo quyệt có thể tạo ra vấn đề nếu các con trỏ khác nhau được căn chỉnh theo các cách cụ thể của máy ... Vấn đề bí ẩn này có thể được cố định bằng cách sử dụng công đoàn". – QuasarDonkey

+0

Nếu các kiểu dữ liệu có sự căn chỉnh khác nhau, bạn cũng có thể gặp vấn đề với chúng trong một liên minh, bởi vì nó không được đảm bảo rằng dữ liệu được đọc từ số nguyên trùng lặp với phao. Nhưng tôi đoán chúng ta có thể có một trình biên dịch cho rằng có thể đặt số nguyên tại địa chỉ X với căn chỉnh 2 và sau đó có phao phù hợp với 4 byte, trong trường hợp nó sẽ bị lỗi hoặc hoạt động kém) BEST (hầu hết) giải pháp di động) có thể lưu trữ dữ liệu dấu chấm động dưới dạng văn bản hoặc dưới dạng điểm cố định ở định dạng số nguyên. Bằng cách đó, không có nghi ngờ gì nó có nghĩa là, hoặc về sự liên kết. –

+0

Điều đó có ý nghĩa, nhưng tôi nghĩ rằng tôi sẽ gắn bó với công đoàn vì nó có vẻ là lựa chọn tốt nhất cho nhị phân. Rất tiếc, tôi không thể sử dụng văn bản vì tôi đang hỗ trợ các tệp từ hệ thống cũ. – QuasarDonkey