2013-03-01 57 views
5

Tôi có một chương trình được viết bằng C++ đang tạo mã nguồn C cho phép tính toán học. Tôi đã nhận thấy rằng các hằng số chiếm rất nhiều không gian trong mã được tạo ra và đang tìm kiếm một đại diện nhỏ gọn hơn.Biểu diễn không mất mát nhỏ gọn của hằng số dấu chấm động trong C/C++

Để tạo hằng, bây giờ tôi đang sử dụng:

double v = ... 
cfile << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v; 

Tôi khá chắc chắn rằng đây là một đại diện lossless, nhưng nó cũng rất cồng kềnh. Ví dụ một số không và một số sẽ được biểu diễn như một cái gì đó như 0,0000000000000000e + 00 và 1,0000000000000000e + 00. Và "0." hoặc "1." mang theo nhiều thông tin.

Có cách nào để in các hằng số để tập tin theo cách gọn nhẹ hơn nhưng vẫn không bị mất? Nó không cần phải xem xét tốt cho một người đọc của con người, chỉ cần biên dịch khi có mặt trong mã C đơn giản (nếu C99, tôi thích nếu nó cũng hợp lệ C++). Hệ thập lục phân có thể là ok nếu nó là di động.

EDIT: Đã xóa std::fixed trong đoạn mã.

+0

Đã lâu rồi, nhưng nhìn [ở đây] (http://en.wikipedia.org/wiki/Huffman_coding), mã hóa Huffman có thể phù hợp với bạn. –

+3

Có lẽ tôi đã hiểu lầm, nhưng sẽ không loại bỏ các số không theo sau là giải pháp? – jogojapan

+0

Liên quan: http://stackoverflow.com/questions/4738768/printing-double-without-losing-precision – jogojapan

Trả lời

3

Đây không phải là vấn đề về thư viện, ngôn ngữ hoặc thư viện chuẩn mà là thuật toán. Nếu bạn có trình tạo mã thì ... tại sao bạn không thay đổi mã được tạo ra để trở thành biểu diễn tốt nhất (= ngắn nhất với độ chính xác yêu cầu)? Đó là những gì bạn làm khi viết mã bằng tay.

Trong put_constant(double value) thói quen giả bạn có thể kiểm tra những gì giá trị mà bạn phải viết:

  • Có một số nguyên? Không làm nổi bật mã bằng std::fixedset_precision, chỉ cần truyền tới số nguyên và thêm dấu chấm.
  • Hãy thử chuyển đổi nó thành chuỗi với cài đặt mặc định sau đó chuyển đổi lại thành double, nếu không có gì thay đổi thì biểu diễn mặc định (ngắn) là đủ tốt.
  • Chuyển đổi chuỗi thành chuỗi với triển khai thực tế của bạn và kiểm tra độ dài của nó. Nếu nó lớn hơn N (xem sau) sử dụng một biểu diễn khác nếu không chỉ cần viết nó.

Có thể (ngắn) biểu diễn cho số dấu phẩy động khi chúng có nhiều chữ số là sử dụng biểu tượng bộ nhớ . Với điều này bạn có một chi phí khá cố định và chiều dài sẽ không bao giờ thay đổi, do đó bạn nên áp dụng nó chỉ cho số lượng rất dài. Một ví dụ ngây thơ để chứng tỏ nó có thể làm việc:

#define USE_L2D __int64 ___tmp = 0; 
#define L2D(x) (double&)(___tmp=x) 

int main(int argc, char* argv[]) 
{ 
    // 2.2 = in memory it is 0x400199999999999A 

    USE_L2D 
    double f1 = L2D(0x400199999999999A); 
    double f2 = 123456.1234567891234567; 

    return 0; 
} 
+1

Tôi chấp nhận điều này làm câu trả lời của tôi. Kiểm tra các bản in khác nhau và chọn cách tốt nhất có lẽ là cách tốt nhất để thực hiện như bạn đề xuất. Cảm ơn! – Joel

-4

Tôi không chắc chắn rằng bạn có thể chuyển các điểm trôi không mất mát như thế này. Điểm nổi là nhất thiết phải mất. Mặc dù chúng có thể đại diện cho một tập hợp con các giá trị chính xác nhưng bạn không thể bao gồm TẤT CẢ các số liệu quan trọng - phần cứng khác nhau có thể có các biểu diễn khác nhau để bạn không thể đảm bảo không mất thông tin. Ngay cả khi bạn có thể vượt qua tất cả trên như giá trị có thể không được đại diện bởi phần cứng nhận.

Nhà điều hành plainstream :: < < sẽ in ra nhiều chữ số theo yêu cầu, do đó, thực sự không cần phải phức tạp.

+0

Tôi không nghĩ câu cuối cùng là chính xác. Độ chính xác mặc định không in nhiều số có thể được biểu diễn trong nội bộ. – jogojapan

+0

Nếu cả người đọc và người viết sử dụng cùng một điểm cho điểm nổi và có cùng số chữ số trong cơ sở đó, bạn có thể đảm bảo truyền chính xác bằng số thập phân, miễn là bạn sử dụng đủ số thập phân chính xác. (Đối với IEEE, 17 chữ số đủ.) –

+0

"Trong khi chúng có thể đại diện cho một tập con của các giá trị chính xác bạn không thể bao gồm TẤT CẢ các số liệu quan trọng" Có bạn có thể. Tại sao bạn không thể? Và bạn không cần phải vượt qua tất cả các chữ số có nghĩa, chỉ đủ để làm cho nó rõ ràng mà số dấu phẩy động có nghĩa là gì. "Phần cứng khác nhau có thể có các biểu diễn khác nhau để bạn không thể đảm bảo không mất thông tin" Đây là lý do tại sao tiêu chuẩn IEEE 754 được xuất bản, ** vào năm 1985 **: để chúng tôi có thể có cùng một biểu diễn trên tất cả các máy tính. Vấn đề bạn đề cập đến đã được giải quyết bằng việc công bố tiêu chuẩn đó hơn 20 năm trước. –

1

Trước tiên, bạn đang mâu thuẫn với chính mình khi bạn lần đầu tiên nói std::scientific, và sau đó std::fixed.Và thứ hai, bạn có lẽ cũng không muốn. Định dạng mặc định thường là được thiết kế để làm điều này tốt nhất. Định dạng mặc định không có tên, cũng không phải là người thao túng, nhưng bạn sẽ nhận được định dạng và có thể được đặt (trong trường hợp mã khác đã đặt định dạng khác) bằng cách sử dụng:

cfile.setf(std::ios_base::fmtflags(), std::ios_base::floatfield); 

Tôi muốn recomment bằng cách sử dụng này. (Bạn vẫn cần độ chính xác, trong số khóa học.)

+0

Ông sử dụng tiêu chuẩn :: cố định và std :: khoa học với nhau để in theo ký hiệu hex để duy trì độ chính xác tối đa trong việc chuyển đổi thành chuỗi. –

+0

@Adriano Đó không phải là những gì nó làm. Người cuối cùng có quyền ưu tiên (trừ khi việc thực thi của anh ta bị phá vỡ nghiêm trọng). –

+0

Bạn nói đúng, tôi thấy sự kết hợp của chúng nhưng nó chỉ ở trong tâm trí (mệt mỏi) của tôi! –

9

Bạn có thể sử dụng dấu phẩy động thập lục phân (The format specifier %a for printf() in C); nó được định nghĩa để bảo toàn tất cả các bit chính xác (C11, 7.21.6.1p8, a,A specifiers).

cfile << std::hexfloat << v; 

Nếu trình biên dịch/thư viện tiêu chuẩn của bạn không hỗ trợ hexfloat, bạn có thể sử dụng C99 %a printf specifier (đây là tương đương, theo quy định trong C++ 11 bảng 88 dưới phần 22.4.2.2.2):

printf("%a", v); 

Ví dụ, chương trình sau đây là C99 hợp lệ:

#include <stdio.h> 
int main() { 
    double v = 0x1.8p+1; 
    printf("%a\n", v); 
} 

file nguồn tạo của bạn sẽ không có giá trị C++ 11 như khá ngớ ngẩn C++ 11 không n ot hỗ trợ hệ thập lục phân dấu chấm động. Tuy nhiên, nhiều trình biên dịch C++ 11 hỗ trợ C99 hexadecimal floating point literals như một phần mở rộng.

+1

Đó là thói quen của trình tạo mã, nó có thể ** kiểm tra ** giá trị để viết để quyết định biểu diễn (= shortes) tốt nhất của nó! –

+0

@ecatmur Cảm ơn con trỏ này và đặc biệt là chỉ ra rằng nó không hợp lệ C++ 11. Bởi vì điều này tôi có lẽ sẽ tránh ký hiệu này vì điều quan trọng là máy phát điện của tôi biên dịch với C++ 11 (và tốt hơn là C++ 03, mà một số người dùng của tôi có). – Joel

Các vấn đề liên quan