2011-12-24 76 views
32

Trong C++ 11 là std::sqrt được định nghĩa là constexpr, tức là nó có thể được sử dụng hợp pháp từ các hàm constexpr khác hoặc trong ngữ cảnh biên dịch thời gian như kích thước mảng hoặc đối số mẫu không? g + + dường như cho phép nó (bằng cách sử dụng -std=c++0x), nhưng tôi không chắc chắn rằng tôi có thể lấy đó là thẩm quyền cho rằng hỗ trợ C++ 0x/C++ 11 vẫn chưa hoàn thành. Thực tế là tôi không thể tìm thấy bất cứ thứ gì trên Internet khiến tôi không chắc chắn.Trong C++ 11 là sqrt được định nghĩa là constexpr?

Có vẻ như đây là thứ mà người ta có thể dễ dàng tìm ra bằng Google, nhưng tôi đã cố gắng (trong 40 phút ngay bây giờ ...) và không thể tìm thấy gì. Tôi có thể tìm thấy một số đề xuất để thêm constexpr vào các phần khác nhau của thư viện chuẩn (ví dụ: this one), nhưng không có gì về sqrt hoặc các chức năng toán học khác.

Trả lời

21

std::sqrt không được định nghĩa là constexpr, theo mục 26.8 của N3291: C++ 11 FDIS (và tôi nghi ngờ họ đã thêm nó vào tiêu chuẩn cuối cùng sau đó). Người ta có thể viết một phiên bản như vậy, nhưng phiên bản thư viện chuẩn không phải là constexpr.

+2

Điều này có được nêu rõ ràng không? 26.8 C thư viện không có tham chiếu đến constexpr. Một triển khai chắc chắn sẽ vẫn phù hợp nếu nó cung cấp các chức năng triển khai hàm constexpr ngoài các yêu cầu bắt buộc mà không có. – user2023370

+6

@ user643722: Việc triển khai thư viện chuẩn * có thể * xác định phiên bản 'constexpr'. Nhưng nó không phải là * yêu cầu * để làm như vậy, và do đó bạn không thể thực sự dựa vào nó. –

22

Chỉ trong trường hợp bất cứ ai quan tâm đến việc một hàm căn bậc hai số nguyên meta, đây là một trong tôi đã viết trong khi cách đây:

constexpr std::size_t isqrt_impl 
    (std::size_t sq, std::size_t dlt, std::size_t value){ 
    return sq <= value ? 
     isqrt_impl(sq+dlt, dlt+2, value) : (dlt >> 1) - 1; 
} 

constexpr std::size_t isqrt(std::size_t value){ 
    return isqrt_impl(1, 3, value); 
} 
+0

Umm, dường như có độ phức tạp tuyến tính, tức là Theta (giá trị), thay vì logarit, điều này khá khả thi, ví dụ: trong câu trả lời của @Linoliumz. – einpoklum

13

Dưới đây là một constexpr vuông thi gốc mà sử dụng tìm kiếm nhị phân. Nó hoạt động chính xác đến 2^64 với gcc và clang, các phiên bản đơn giản khác thường không thành công cho số> 2^32 vì trình biên dịch giới hạn độ sâu đệ quy, ví dụ: 200.

// C++11 compile time square root using binary search 

#define MID ((lo + hi + 1)/2) 

constexpr uint64_t sqrt_helper(uint64_t x, uint64_t lo, uint64_t hi) 
{ 
    return lo == hi ? lo : ((x/MID < MID) 
     ? sqrt_helper(x, lo, MID - 1) : sqrt_helper(x, MID, hi)); 
} 

constexpr uint64_t ct_sqrt(uint64_t x) 
{ 
    return sqrt_helper(x, 0, x/2 + 1); 
} 

Dưới đây là một phiên bản đẹp hơn (đối với các hằng số nguyên) mà đòi hỏi C++ 14, nó cũng tương tự như một trong những trình bày trong Baptiste Wicht của blog post. Các hàm constexpr C++ 14 được phép sử dụng các biến cục bộ và các câu lệnh if.

// C++14 compile time square root using binary search 

template <typename T> 
constexpr T sqrt_helper(T x, T lo, T hi) 
{ 
    if (lo == hi) 
    return lo; 

    const T mid = (lo + hi + 1)/2; 

    if (x/mid < mid) 
    return sqrt_helper<T>(x, lo, mid - 1); 
    else 
    return sqrt_helper(x, mid, hi); 
} 

template <typename T> 
constexpr T ct_sqrt(T x) 
{ 
    return sqrt_helper<T>(x, 0, x/2 + 1); 
} 
+0

Tại sao 'MID' là macro? – dyp

+0

Bởi vì trong C++ 11 biến const tĩnh bên trong các hàm constexpr không được phép. – Linoliumz

+0

Đây có phải là chương trình hay mọi người thực sự sử dụng thứ gì đó chiếm hơn 200 cuộc khảo sát để biên dịch không? – Mikhail

3

Nếu chúng ta nhìn vào các dự thảo tiêu chuẩn gần 11 N3337 C++ chúng ta có thể thấy rằng sqrt không được đánh dấu constexpr, từ phần 26.8c.math:

Nội dung của các tiêu đề này giống với tiêu đề thư viện chuẩn C và tương ứng, với thay đổi sau đây:

không có thay đổi nào bao gồm việc thêm constexpr đến sqrt.

Chúng ta có thể thấy từ câu hỏi Is gcc considering builtins of non-constant expression functions to be constant expressions, rằng gcc đánh dấu nhiều hàm toán học là constexpr làm tiện ích mở rộng. Tiện ích mở rộng này là non-conforming extension, như tôi lưu ý trong câu trả lời của mình cho câu hỏi được liên kết khi số gcc triển khai nó trông giống như một tiện ích mở rộng phù hợp nhưng thay đổi này và gcc có khả năng khắc phục tiện ích mở rộng này.

9

Dưới đây là triển khai constexpr nhanh và hiệu quả cho double số dấu phẩy động. Bạn cũng có thể điều chỉnh nó thành float nếu cần:

#include <limits> 

namespace Detail 
{ 
    double constexpr sqrtNewtonRaphson(double x, double curr, double prev) 
    { 
     return curr == prev 
      ? curr 
      : sqrtNewtonRaphson(x, 0.5 * (curr + x/curr), curr); 
    } 
} 

/* 
* Constexpr version of the square root 
* Return value: 
* - For a finite and non-negative value of "x", returns an approximation for the square root of "x" 
* - Otherwise, returns NaN 
*/ 
double constexpr sqrt(double x) 
{ 
    return x >= 0 && x < std::numeric_limits<double>::infinity() 
     ? Detail::sqrtNewtonRaphson(x, x, 0) 
     : std::numeric_limits<double>::quiet_NaN(); 
} 
Các vấn đề liên quan