2012-02-10 21 views
15

Tôi muốn in một giá trị gấp đôi để std::cout portably (GCC, clang, MSVC++) sao cho đầu ra giống nhau trên tất cả các nền tảng.In ấn di động của số mũ của một đôi để C + + iostreams

Tôi gặp sự cố với định dạng số mũ. sau Chương trình

#include <iostream> 
int main() 
{ 
    std::cout << 0.1e-7 << std::endl; 
    return 0; 
} 

Có đầu ra này với GCC:

1e-08 

và đầu ra sau đây với MSVC

1e-008 

Làm thế nào tôi có thể làm cho cả hai kết quả đầu ra giống nhau không?

Tôi xin lỗi nếu đây là câu hỏi ngớ ngẩn nhưng tôi chưa tìm thấy câu trả lời. Tất cả các định dạng dường như phát triển xung quanh các định dạng của tất cả mọi thứ trước khi mantissa ...

EDIT: Kết quả của GCC là 1e-08 không 1e-8 (như đã nêu ban đầu) nên nó phù hợp. Xin lỗi vì sự nhầm lẫn.

EDIT2: Trên thực tế được đổi tên thành "mantissa" thành "số mũ" theo nhận xét của Dietmar. There also is a section on Wikipedia on mantissa vs. significant.

+0

Bạn đã nhìn [thao tác] (http://www.cplusplus.com/reference/iostream/manipulators/)? – razlebe

+1

@razlebe: Tôi không thể tìm thấy câu trả lời khi sử dụng các thao tác. – Manuel

+0

Tôi thấy GCC không phù hợp vì nó in '1.e-08' và' 1.e-18' (hai chữ số) nhưng nó in '1.e-256' (ba chữ số). Tôi không thể tìm thấy một thư viện dòng mà giải quyết điều này (tôi đã thử với iostream của khóa học và Boost.Format). Vì vậy, nếu một người muốn có chiều rộng cố định tăng gấp đôi thì cần phải đặt trước và tăng thêm không gian cho số thứ ba có thể của số mũ. – alfC

Trả lời

11

Không có người thao túng nào kiểm soát định dạng của số mũ (tôi giả sử bạn có nghĩa là số mũ thay vì phần định trị), tên "chính thức" được sử dụng cho phần định trị là có nghĩa là). Để làm cho vấn đề tồi tệ hơn, tôi không thể thấy bất kỳ quy tắc nào trong tiêu chuẩn C mà hạn chế định dạng của số mũ. Tôi nhận ra rằng đây là về C++ nhưng với mục đích của các chi tiết định dạng, tiêu chuẩn C++ đề cập đến tiêu chuẩn C.

Cách tiếp cận duy nhất mà tôi biết là sử dụng một khía cạnh riêng của std::num_put<char> định dạng các giá trị như mong muốn. Khía cạnh này sau đó sẽ được đưa vào một số std::locale mà lần lượt là imbue() ed thành std::cout. Triển khai tiềm năng có thể sử dụng mặt nạ std::num_put<char> mặc định (hoặc snprintf(), thật không may, có lẽ đơn giản hơn) để định dạng số dấu phẩy động và sau đó tách số 0 đứng đầu khỏi số mũ.

+4

Định dạng C++ được định nghĩa theo thuật ngữ 'printf', có nghĩa là" Số mũ phải luôn chứa ít nhất hai chữ số. " (Vì vậy, g + + là không phù hợp.) Tôi đang đọc từ các tiêu chuẩn Posix ở đây, được cho là phù hợp với tiêu chuẩn C. Nhưng tôi có những ký ức mơ hồ về văn bản nói rằng nó không được nhiều hơn hai ký tự trừ khi cần thiết (điều này cũng sẽ khiến VC sai); Tôi nhớ đã thảo luận về điều này một thời gian dài, trước đây, và nó đã được thành lập rằng VC là không phù hợp (mà không giải quyết được vấn đề nếu đó là trình biên dịch bạn phải sử dụng). –

+1

@JamesKanze là nó có thể đọc nó như thể họ có nghĩa là "nhân vật" thay vì chữ số trong trường hợp dấu trừ sẽ được tính? – Flexo

+0

Tôi chỉ xem nhanh tiêu chuẩn C99 (Tôi không có phiên bản mới hơn) và tôi không tìm thấy bất kỳ quy tắc định dạng nào cho số mũ. Trong mọi trường hợp, bất cứ điều gì là phù hợp hoặc không phù hợp, có vẻ như có một số quyền tự do thực hiện và chắc chắn có sự thay đổi trong việc thực hiện. Tôi vẫn nghĩ rằng phương pháp được mô tả ở trên có lẽ là cách tiếp cận dễ dàng nhất để thay đổi rõ ràng định dạng được tạo ra. –

3

Trong khi câu trả lời của Dietmar là câu trả lời sạch và có lẽ chỉ thực sự di động, tôi vô tình tìm thấy câu trả lời nhanh và bẩn: MSVC cung cấp chức năng _set_output_format để chuyển sang "số mũ in dưới dạng hai chữ số".

Lớp RAII sau đây có thể được khởi tạo trong hàm main() của bạn để cung cấp cho bạn cùng một hành vi của GCC, CLANG và MSVC.

class ScientificNotationExponentOutputNormalizer 
{ 
public: 
    unsigned _oldExponentFormat; 

    ScientificNotationExponentOutputNormalizer() : _oldExponentFormat(0) 
    { 
#ifdef _MSC_VER 
     // Set scientific format to print two places. 
     unsigned _oldExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT); 
#endif 
    } 

    ~ScientificNotationExponentOutputNormalizer() 
    { 
#ifdef _MSC_VER 
     // Enable old exponent format. 
     _set_output_format(_oldExponentFormat); 
#endif 
    } 
}; 
+0

Điều này đòi hỏi ít nhất MSVCR80.dll gây đau đớn khi thiết lập để làm việc với MinGW. – Manuel

+0

Lưu ý: như đã nêu trong tài liệu, 'Chức năng này đã lỗi thời. Bắt đầu từ Visual Studio 2015, nó không có sẵn trong CRT.' – BenC

2

Vấn đề là Visual C++ không tuân thủ tiêu chuẩn C99. Trong Visual C++ 2015, _set_output_format đã được gỡ bỏ từ trình biên dịch hiện nay theo tiêu chuẩn:

Các %e%E định dạng specifiers định dạng một số dấu chấm động như một mantissa thập phân và số mũ. Các số định dạng %g%G cũng định dạng các số trong biểu mẫu này trong một số trường hợp. Trong các phiên bản trước, CRT sẽ luôn tạo các chuỗi với các số mũ ba chữ số.Ví dụ: printf("%e\n", 1.0) sẽ in 1.000000e+000. Điều này không chính xác: C yêu cầu rằng nếu số mũ chỉ thể hiện bằng một hoặc hai chữ số, thì chỉ có hai chữ số được in.

Trong Visual Studio 2005, công tắc tuân thủ chung được thêm vào: _set_output_format. Một chương trình có thể gọi hàm này với đối số _TWO_DIGIT_EXPONENT, để cho phép in số mũ phù hợp. Hành vi mặc định đã được thay đổi sang chế độ in số mũ tuân thủ tiêu chuẩn.

Xem Breaking Changes in Visual C++ 2015. Đối với các phiên bản cũ hơn, hãy xem câu trả lời của @ Manuel.

FYI, trong C99 standard, chúng ta có thể đọc:

e, E

Một lập luận đôi đại diện cho một số dấu chấm động được chuyển đổi trong phong cách [-] d.ddd e (+ -) dd, trong đó có một chữ số (không phải là số không nếu đối số là nonzero) trước ký tự dấu thập phân và số chữ số sau khi nó bằng với độ chính xác; nếu độ chính xác bị thiếu, nó được lấy là 6; nếu độ chính xác bằng không và cờ # không được chỉ định, thì sẽ không có ký tự dấu thập phân nào xuất hiện. Giá trị được làm tròn thành số chữ số thích hợp. Trình chỉ định chuyển đổi E tạo ra một số có E thay vì e giới thiệu số mũ. Số mũ luôn chứa ít nhất hai chữ số và chỉ càng nhiều chữ số càng cần thiết để biểu thị số mũ. Nếu giá trị bằng 0, số mũ bằng không. Đối số kép đại diện cho một vô hạn hoặc NaN được chuyển đổi theo kiểu của một trình biến đổi f hoặc F.

Đây là sự khác biệt so với C90 không đưa ra bất kỳ dấu hiệu nào liên quan đến độ dài yêu cầu của số mũ.

Lưu ý rằng C++ thay đổi trực quan gần đây cũng liên quan đến cách in nan, inf, vv

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