2015-09-28 45 views
6

Ngôn ngữ C99 có khả năng xác định trực tiếp số mũ và số nguyên của một dấu chấm động nhị phân (được gọi là "hexfloats"), ví dụ: 0x1.0p01 * pow(2, 0) hoặc 1.0. Tiêu chuẩn C++ 11 bao gồm thư viện chuẩn C99, bao gồm khả năng tuần tự hóa và deserialize hexfloats từ các chuỗi, nhưng đối với một số lý do bí ẩn không bao gồm các bản thân chữ.C++ hexfloat biên dịch phân tích cú pháp thời gian

(1) Tại sao ủy ban ngôn ngữ không thêm tính năng rất đơn giản này là khá cần thiết cho tính toán số?

(2) Làm cách nào để triển khai phân tích cú pháp hexfloat biên dịch trong tập con C++ 11 được Visual Studio 2013 hỗ trợ? GCC cho phép các ký tự hexfloat trong C++, vì vậy đây không phải là vấn đề trong thế giới GNU.

EDIT: Rõ ràng hexfloats không thể được thêm vào C++ 11 vì nó sẽ xung đột với chữ "p" do người dùng định nghĩa. Đặc biệt là mỉa mai rằng một tính năng thực sự hữu ích không thể được thực hiện vì một cái gì đó mà không ai sử dụng (UDLs).

+0

VS0213 không phải là hoàn toàn C++ 11 đơn khiếu nại. bạn có thể muốn thử VS2015. – NathanOliver

+0

@NathanOliver, tôi biết. Nó thiếu 'constexpr', điều này sẽ làm cho việc triển khai thực hiện khá khó khăn. Thật không may, tôi cần hỗ trợ VS2013, vì một số thư viện tôi đang sử dụng không hoạt động với VS2015. – 68ejxfcj5669

+0

Để cụ thể, bạn có yêu cầu gì đó dọc theo các dòng của 'double pi = 0x1.921fb54442d18p + 1;'? –

Trả lời

3

Đối với những người cần tiêu chuẩn constexpr chữ nổi hexadecimal hoặc chức năng tương đương (hoặc chỉ quan tâm đến phân tích cú pháp tại thời gian biên dịch :)), đây là giải pháp cho C++ 11 trở lên. Tuy nhiên, nó không ở dạng các chữ cái do người dùng định nghĩa, nhưng rất gần. Cách sử dụng giống như HEX_LF_C(0x3.02ca7b893217bp-3456) để tạo thành chữ là 0x3.02ca7b893217bp-3456.

Tên duy nhất trong không gian tên chung được đặt trước ở đây là HEX_LF_C do tên lớp và macro.

Để có phiên bản đơn giản hơn nhiều cho C++ 11 và thậm chí dễ đọc hơn đối với C++ 14, nhưng ít sử dụng đơn giản hơn, hãy xem version 6 of this answer.

Bây giờ ở đây đi mã (và here's its live demo):

class HEX_LF_C 
{ 
    using size_t=decltype(sizeof(0)); // avoid including extra headers 
    static constexpr const long double _0x1p256=1.15792089237316195424e77L; // 2^256 
    struct BadDigit{}; 
    // Unportable, but will work for ANSI charset 
    static constexpr int hexDigit(char c) 
    { 
     return '0'<=c&&c<='9' ? c-'0' : 
       'a'<=c&&c<='f' ? c-'a'+0xa : 
       'A'<=c&&c<='F' ? c-'A'+0xA : throw BadDigit{}; 
    } 
    // lightweight constexpr analogue of std::strtoull 
    template<typename Int> 
    static constexpr Int getNumber(const char* array, 
            int base, 
            size_t begin, 
            size_t end, 
            Int accumulated=Int(0)) 
    { 
     return begin==end ? accumulated : 
       array[begin]=='-' ? -getNumber<Int>(array,base,begin+1,end) : 
       array[begin]=='+' ? +getNumber<Int>(array,base,begin+1,end) : 
       getNumber<Int>(array,base,begin+1,end, 
            accumulated*base+hexDigit(array[begin])); 
    } 
    // lightweight constexpr version of std::scalbn 
    static constexpr long double scalbn(long double value, int exponent) 
    { 
     // Trying hard to avoid hitting compiler recursion limit 
     return exponent==0 ? value : exponent>0 ? 
      (exponent>+255 ? scalbn(value*_0x1p256,exponent-256) : scalbn(value*2,exponent-1)) : 
      (exponent<-255 ? scalbn(value/_0x1p256,exponent+256) : scalbn(value/2,exponent+1)); 
    } 
    // constexpr version of std::strlen 
    static constexpr size_t strlen(const char* array) 
    { return *array ? 1+strlen(array+1) : 0; } 
    static constexpr size_t findChar(const char* array, 
            char charToFind, 
            size_t begin, 
            size_t end) 
    { 
     return begin==end ? end : 
       array[begin]==charToFind ? begin : 
       findChar(array,charToFind,begin+1,end); 
    } 
    static constexpr size_t mantissaEnd(const char* str) 
    { return findChar(str,'p',0,strlen(str)); } 

    static constexpr size_t pointPos(const char* str) 
    { return findChar(str,'.',0,mantissaEnd(str)); } 

    static constexpr int exponent(const char* str) 
    { 
     return mantissaEnd(str)==strlen(str) ? 0 : 
       getNumber<int>(str,10,mantissaEnd(str)+1,strlen(str)); 
    } 
    static constexpr bool isSign(char ch) { return ch=='+'||ch=='-'; } 
    static constexpr size_t mantissaBegin(const char* str) 
    { 
     return isSign(*str)+ 
       2*(str[isSign(*str)]=='0' && str[isSign(*str)+1]=='x'); 
    } 
    static constexpr unsigned long long beforePoint(const char* str) 
    { 
     return getNumber<unsigned long long>(str, 
              16, 
              mantissaBegin(str), 
              pointPos(str)); 
    } 
    static constexpr long double addDigits(const char* str, 
              size_t begin, 
              size_t end, 
              long double currentValue, 
              long double currentFactor) 
    { 
     return begin==end ? currentValue : 
       addDigits(str,begin+1,end, 
         currentValue+currentFactor*hexDigit(str[begin]), 
         currentFactor/16); 
    } 
    // If you don't need to force compile-time evaluation, you can use this 
    // directly (having made it public) 
    template<size_t N> 
    static constexpr long double get(const char (&str)[N]) 
    { 
     return (str[0]=='-' ? -1 : 1)* 
      addDigits(str,pointPos(str)+1,mantissaEnd(str), 
         scalbn(beforePoint(str),exponent(str)), 
         scalbn(1.L/16,exponent(str))); 
    } 
    struct UnsupportedLiteralLength{}; 
public: 
    // This helps to convert string literal to a valid template parameter 
    // It just packs the given chunk (8 chars) of the string into a ulonglong. 
    // We rely here and in LF_Evaluator on the fact that 32 chars is enough 
    // for any useful long double hex literal (on x87 arch). 
    // Will need tweaking if support for wider long double types is required. 
    template<size_t N> 
    static constexpr unsigned long long string_in_ull(const char (&array)[N], 
                 size_t start, 
                 size_t end, 
                 size_t numIndex) 
    { 
     // relying on CHAR_BIT==8 here 
     return N>32 ? throw UnsupportedLiteralLength{} : 
       start==end || start>=N ? 0 : 
       string_in_ull(array,start+1,end,numIndex) | 
        ((array[start]&0xffull)<<(8*(start-numIndex))); 
    } 
    // This is to force compile-time evaluation of the hex constant 
    template<unsigned long long A, 
      unsigned long long B, 
      unsigned long long C, 
      unsigned long long D> 
    struct LF_Evaluator 
    { 
     static constexpr char ch(unsigned long long X, 
           int charIndex) { return X>>charIndex*8; } 
     static constexpr const char string[32]={ 
      ch(A,0),ch(A,1),ch(A,2),ch(A,3),ch(A,4),ch(A,5),ch(A,6),ch(A,7), 
      ch(B,0),ch(B,1),ch(B,2),ch(B,3),ch(B,4),ch(B,5),ch(B,6),ch(B,7), 
      ch(C,0),ch(C,1),ch(C,2),ch(C,3),ch(C,4),ch(C,5),ch(C,6),ch(C,7), 
      ch(D,0),ch(D,1),ch(D,2),ch(D,3),ch(D,4),ch(D,5),ch(D,6),ch(D,7) 
      }; 
     static constexpr long double value=get(string); 
    }; 
}; 

#define HEX_LF_C(num) HEX_LF_C::LF_Evaluator<     \ 
         HEX_LF_C::string_in_ull(#num,0,8,0),  \ 
         HEX_LF_C::string_in_ull(#num,8,16,8), \ 
         HEX_LF_C::string_in_ull(#num,16,24,16), \ 
         HEX_LF_C::string_in_ull(#num,24,32,24)>::value 

// Now some usage examples 

#include <iostream> 
#include <iomanip> 

int main() 
{ 
    enum { constexprTest=static_cast<int>(HEX_LF_C(0x2.34f7a87dp+19)) }; 
    std::cout << "constexpr test: " << constexprTest << "\n"; 
    std::cout << "value expected: " << 1157053 << "\n"; 

    std::cout << "need: 0x9.234f87ac95p+954\n"; 
    std::cout << "got : " << std::hexfloat << HEX_LF_C(0x9.234f87ac95p+954) << "\n"; 

    std::cout << "need: -0x9.00234f87ac95p-1954\n"; 
    std::cout << "got : " << std::hexfloat << HEX_LF_C(-0x9.00234f87ac95p-1954) << "\n"; 

#if defined __GNUG__ && !defined __clang__ // clang emits errors in pedantic mode 
    static_assert(0x3.02ca7b893217bp-3456L==HEX_LF_C(0x3.02ca7b893217bp-3456), 
        "Something is broken"); 
    std::cout << std::boolalpha << "Works correctly as compared to GCC? " 
       << (0x3.4d0a89f5c217bp-3456L==HEX_LF_C(0x3.4d0a89f5c217bp-3456)) << "\n"; 
#endif 
    std::cout << "0x" << "9.2f3ca523p+73" << "\n"; 
    constexpr auto x=HEX_LF_C(9.2f3ca523p+73); 
    std::cout << std::hexfloat << x << "\n"; 
} 
7

Tiêu chuẩn C++ 11 bao gồm các thư viện chuẩn C99, bao gồm khả năng serialize và deserialize hexfloats từ chuỗi, nhưng vì một lý do bí ẩn không bao gồm themslves literals.

Việc in và mã hóa chữ cái không phải là một phần của thư viện chuẩn, vì vậy chỉ đề cập đến thư viện chuẩn C99 trong thư viện chuẩn C++ không có nghĩa là các tính năng ngôn ngữ riêng lẻ cũng được bao gồm trong C++.

(1) Tại sao Ủy ban ngôn ngữ không thêm tính năng rất đơn giản này là khá cần thiết cho tính toán số?

Vì không ai đề xuất nó để đưa vào C++. Mọi thứ không chỉ xuất hiện một cách kỳ diệu trong tiêu chuẩn C++ vì chúng ở trong chuẩn C. Ai đó phải đề xuất nó và tranh luận ủng hộ nó.

+0

Nghiêm túc? Đây là một tính năng rõ ràng là cần thiết kể từ năm 1963, tối thiểu (ví dụ, E. Lorenz, * Xác định dòng không tuần hoàn *). Nó là một tính năng được sử dụng rộng rãi trong nhiều triển khai thư viện toán học C. Cộng đồng C đã thấy sự cần thiết cho điều này đôi khi trong thiên niên kỷ trước. Đây là một sự thất bại abject của ủy ban C++. –

+3

@DavidHammen, tôi nói nó không cần thiết ở đâu? Tôi nói không ai đề xuất nó để đưa vào, vì vậy nó không được bao gồm. Bạn không đồng ý với điều gì hoặc nghĩ rằng tôi không nghiêm túc? Tôi nghĩ rằng đó là một điều đáng tiếc thực sự, tôi muốn chúng tôi đã có chúng trong C + +. –

+0

Ai đó gần đây đã đề xuất nó để đưa vào tiêu chuẩn C++ và ủy ban đã bỏ phiếu để bao gồm nó. Như tôi đã nói ở trên, mọi thứ không chỉ xuất hiện một cách kỳ diệu, nhưng nếu ai đó đề nghị họ thì họ có thể trở thành một phần của tiêu chuẩn. Đó là cách quy trình được cho là hoạt động. –

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