Có chức năng ký hiệu chuẩn (dấu hiệu, sgn) trong C / C ++ không?


409

Tôi muốn một hàm trả về -1 cho số âm và +1 cho số dương. http://en.wikipedia.org/wiki/Sign_function Thật dễ dàng để viết riêng của tôi, nhưng có vẻ như một cái gì đó phải có trong một thư viện tiêu chuẩn ở đâu đó.

Chỉnh sửa: Cụ thể, tôi đang tìm kiếm một chức năng làm việc trên phao.


13
Nó nên trả về 0 là gì?
Craig McQueen

61
@Craig McQueen; điều đó phụ thuộc vào việc nó là số 0 dương hay số 0 âm.
ysth

1
Tôi nhận thấy rằng bạn đã chỉ định giá trị trả về là một số nguyên. Bạn đang tìm kiếm một giải pháp lấy số nguyên hoặc số dấu phẩy động?
Mark Byers

6
@ysth @Craig McQueen, sai cho phao quá, không? Định nghĩa của sgn (x) nói trả về 0 nếu x==0. Theo IEEE 754 , số 0 âm và số 0 dương nên so sánh bằng nhau.
RJFalconer

5
@ysth "nó phụ thuộc vào số 0 dương hoặc số 0 âm". Sự thật là nó không như vậy.
RJFalconer

Câu trả lời:


506

Ngạc nhiên là chưa có ai đăng phiên bản C ++ an toàn kiểu:

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

Những lợi ích:

  • Trên thực tế thực hiện dấu hiệu (-1, 0 hoặc 1). Việc triển khai ở đây bằng copysign chỉ trả về -1 hoặc 1, không phải là dấu hiệu. Ngoài ra, một số triển khai ở đây đang trả về một float (hoặc T) thay vì int, điều này có vẻ lãng phí.
  • Hoạt động cho ints, float, double, unsign hoặc bất kỳ loại tùy chỉnh nào có thể xây dựng từ số nguyên 0 và có thể đặt hàng.
  • Nhanh! copysignlà chậm, đặc biệt là nếu bạn cần quảng bá và sau đó thu hẹp lại. Điều này là không chi nhánh và tối ưu hóa xuất sắc
  • Tuân thủ tiêu chuẩn! Hack bithift gọn gàng, nhưng chỉ hoạt động đối với một số biểu diễn bit và không hoạt động khi bạn có loại không dấu. Nó có thể được cung cấp như một chuyên môn thủ công khi thích hợp.
  • Chính xác! So sánh đơn giản với số không có thể duy trì biểu diễn độ chính xác cao bên trong của máy (ví dụ 80 bit trên x87) và tránh vòng sớm đến 0.

Hãy cẩn thận:

  • Đó là một mẫu để có thể mất nhiều thời gian hơn để biên dịch trong một số trường hợp.
  • Rõ ràng một số người nghĩ rằng việc sử dụng một chức năng thư viện tiêu chuẩn mới, hơi bí truyền và rất chậm mà thậm chí không thực sự thực hiện dấu hiệu là điều dễ hiểu hơn.
  • Một < 0phần của kiểm tra kích hoạt -Wtype-limitscảnh báo của GCC khi được khởi tạo cho loại không dấu. Bạn có thể tránh điều này bằng cách sử dụng một số quá tải:

    template <typename T> inline constexpr
    int signum(T x, std::false_type is_signed) {
        return T(0) < x;
    }
    
    template <typename T> inline constexpr
    int signum(T x, std::true_type is_signed) {
        return (T(0) < x) - (x < T(0));
    }
    
    template <typename T> inline constexpr
    int signum(T x) {
        return signum(x, std::is_signed<T>());
    }
    

    (Đó là một ví dụ tốt về cảnh báo đầu tiên.)


18
@GMan: Chỉ GCC mới đây (4.5) đã ngừng có chi phí bậc hai so với số lần khởi tạo cho các hàm mẫu và chúng vẫn đắt hơn khi phân tích cú pháp và khởi tạo so với các hàm được viết thủ công hoặc bộ tiền xử lý C tiêu chuẩn. Trình liên kết cũng phải thực hiện nhiều công việc hơn để loại bỏ các cảnh báo trùng lặp. Các mẫu cũng khuyến khích # gồm-in- # bao gồm, điều này làm cho việc tính toán phụ thuộc mất nhiều thời gian hơn và nhỏ hơn (thường là thực hiện, không phải giao diện) để buộc nhiều tệp được biên dịch lại.

15
@Joe: Vâng, và vẫn không có chi phí đáng chú ý. C ++ sử dụng các mẫu, đó chỉ là thứ mà tất cả chúng ta phải hiểu, chấp nhận và vượt qua.
GManNickG

42
Đợi đã, cái gì là "copysign là chậm" kinh doanh ...? Sử dụng các trình biên dịch hiện tại (g ++ 4.6+, clang ++ 3.0), std::copysigndường như dẫn đến mã tuyệt vời cho tôi: 4 hướng dẫn (nội tuyến), không phân nhánh, hoàn toàn sử dụng FPU. Ngược lại, công thức được đưa ra trong câu trả lời này tạo ra mã tồi tệ hơn nhiều (nhiều hướng dẫn khác, bao gồm nhân, di chuyển qua lại giữa đơn vị số nguyên và FPU) ...
snogglethorpe

14
@snogglethorpe: Nếu bạn đang gọi copysignint thì nó sẽ tăng / tăng gấp đôi và phải thu hẹp lại khi trả về. Trình biên dịch của bạn có thể tối ưu hóa quảng cáo đó nhưng tôi không thể tìm thấy bất cứ điều gì cho thấy tiêu chuẩn được đảm bảo. Ngoài ra để thực hiện đăng nhập thông qua copysign, bạn cần xử lý thủ công 0 trường hợp - vui lòng đảm bảo bạn đưa nó vào bất kỳ so sánh hiệu suất nào.

53
Phiên bản đầu tiên không phân nhánh. Tại sao mọi người nghĩ rằng một so sánh được sử dụng trong một biểu thức sẽ không tạo ra một nhánh? Nó sẽ trên hầu hết các kiến ​​trúc. Chỉ các bộ xử lý có cmove (hoặc tiền định) mới tạo mã không phân nhánh, nhưng chúng cũng sẽ làm điều đó cho các ternary hoặc nếu / khác nếu đó là một chiến thắng.
Patrick Schlüter

271

Tôi không biết về một chức năng tiêu chuẩn cho nó. Đây là một cách thú vị để viết nó mặc dù:

(x > 0) - (x < 0)

Đây là một cách dễ đọc hơn để làm điều đó:

if (x > 0) return 1;
if (x < 0) return -1;
return 0;

Nếu bạn thích toán tử ternary, bạn có thể làm điều này:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)

7
Đánh dấu tiền chuộc, biểu thức của bạn cho kết quả không chính xác cho x==0.
avakar

3
@Svante: "Mỗi toán tử <, >... sẽ mang lại 1 nếu quan hệ được chỉ định là đúng và 0 nếu sai"
Stephen Canon

11
@Svante: không chính xác. Giá trị 0là "sai"; bất kỳ giá trị nào khác là "đúng"; tuy nhiên, các toán tử quan hệ và đẳng thức luôn trả về 0hoặc 1(xem Tiêu chuẩn 6.5.8 và 6.5.9). - giá trị của biểu thức a * (x == 42)0hoặc a.
PMG

21
Dấu hiệu suất cao, tôi rất ngạc nhiên khi bạn bỏ lỡ thẻ C ++. Câu trả lời này rất hợp lệ và không xứng đáng để bỏ phiếu. Hơn nữa, tôi sẽ không sử dụng copysigntích hợp xngay cả khi tôi có sẵn.
avakar

6
Có ai thực sự đã kiểm tra mã GCC / G ++ / bất kỳ trình biên dịch nào khác phát ra trên một nền tảng thực sự chưa? Tôi đoán là phiên bản "không nhánh" sử dụng hai nhánh thay vì một. Bitshifting có lẽ nhanh hơn rất nhiều - và dễ mang theo hơn về mặt hiệu suất.
Jørgen Fogh

192

Có một hàm thư viện toán học C99 được gọi là copysign (), lấy dấu từ một đối số và giá trị tuyệt đối từ đối số khác:

result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double

sẽ cho bạn kết quả là +/- 1.0, tùy thuộc vào dấu hiệu của giá trị. Lưu ý rằng các số 0 dấu phẩy động được ký: (+0) sẽ mang lại +1 và (-0) sẽ mang lại -1.


57
Upvote này, hạ thấp câu trả lời phổ biến nhất. Trái quay cuồng trong kinh ngạc rằng cộng đồng SO dường như thích hack để sử dụng chức năng thư viện tiêu chuẩn. Có thể các vị thần lập trình lên án tất cả các bạn cố gắng giải mã các bản hack được sử dụng bởi các lập trình viên thông minh không quen thuộc với các tiêu chuẩn ngôn ngữ. Vâng, tôi biết điều này sẽ tiêu tốn của tôi một tấn đại diện cho SO, nhưng tôi thích ở bên cạnh cơn bão hơn so với phần còn lại của bạn ...
Dấu hiệu suất cao

34
Điều này gần đúng, nhưng nó đưa ra câu trả lời sai cho số không (ít nhất là theo bài viết trên Wikipedia trong câu hỏi). Đề nghị tốt đẹp mặc dù. +1 dù sao đi nữa.
Mark Byers

4
Nếu bạn muốn một số nguyên hoặc nếu bạn muốn kết quả chính xác cho số không, tôi thích câu trả lời của Mark Byers, đó là sự tao nhã! Nếu bạn không quan tâm đến những điều trên, copysign () có thể có một tiến bộ về hiệu suất, tùy thuộc vào ứng dụng - nếu tôi đang tối ưu hóa một vòng lặp quan trọng, tôi sẽ thử cả hai.
sắp tới

10
1) C99 không được hỗ trợ đầy đủ ở mọi nơi (xem xét VC ++); 2) đây cũng là một câu hỏi C ++. Đây là một câu trả lời tốt, nhưng câu trả lời nâng cao cũng hoạt động, và được áp dụng rộng rãi hơn.
Pavel Minaev

5
Cứu tinh! Cần một cách để xác định giữa -0.0 và 0.0
Ólafur

79

Có vẻ như hầu hết các câu trả lời đã bỏ lỡ câu hỏi ban đầu.

Có chức năng ký hiệu chuẩn (dấu hiệu, sgn) trong C / C ++ không?

Không phải trong thư viện tiêu chuẩn, tuy nhiên copysign, có thể được sử dụng gần như cùng một cách thông qua copysign(1.0, arg)và có một chức năng ký hiệu thực sự boost, cũng có thể là một phần của tiêu chuẩn.

    #include <boost/math/special_functions/sign.hpp>

    //Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
    template <class T>
    inline int sign (const T& z);

http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_fifts.html


5
Đây phải là câu trả lời được bình chọn nhiều nhất, vì nó đưa ra giải pháp gần nhất có thể cho những gì được hỏi trong câu hỏi.
BartoszKP 20/03/2015

Tôi đã tự hỏi trong vài phút qua tại sao thư viện tiêu chuẩn không có chức năng ký hiệu. Nó rất phổ biến - chắc chắn được sử dụng phổ biến hơn chức năng gamma có thể được tìm thấy trong tiêu đề cmath.
Taozi

4
Lời giải thích tôi thường nhận được cho các câu hỏi tương tự là "đủ dễ để tự thực hiện" IMO không phải là lý do chính đáng. Nó hoàn toàn tin tưởng vào các vấn đề về tiêu chuẩn hóa, các trường hợp cạnh không rõ ràng và nơi để đặt một công cụ được sử dụng rộng rãi như vậy.
Catskul

77

Rõ ràng, câu trả lời cho câu hỏi ban đầu của người đăng là không. Không có chức năng C ++ tiêu chuẩnsgn .


2
@SR_ Bạn không đúng. copysign()sẽ không tạo tham số đầu tiên của bạn 0,0 nếu thứ hai là 0,0. Nói cách khác, John là chính xác.
Alexis Wilke

30

Có chức năng ký hiệu chuẩn (dấu hiệu, sgn) trong C / C ++ không?

Có, tùy thuộc vào định nghĩa.

C99 và sau này có signbit()macro trong<math.h>

int signbit(nổi thật x);
Các signbitlợi vĩ mô một giá trị khác khi và chỉ khi các dấu hiệu của giá trị đối số của nó là tiêu cực. C11 §7.12.3.6


Tuy nhiên, OP muốn một chút gì đó khác biệt.

Tôi muốn một hàm trả về -1 cho số âm và +1 cho số dương. ... một chức năng làm việc trên phao.

#define signbit_p1_or_n1(x)  ((signbit(x) ?  -1 : 1)

Sâu sắc hơn:

Bài viết không cụ thể trong các trường hợp sau : x = 0.0, -0.0, +NaN, -NaN.

Một signum()lợi nhuận cổ điển +1trên x>0, -1trên x<00trên x==0.

Nhiều câu trả lời đã bao gồm điều đó, nhưng không giải quyết x = -0.0, +NaN, -NaN. Nhiều người đang hướng đến một quan điểm số nguyên thường thiếu Not-a-Numbers ( NaN ) và -0.0 .

Chức năng trả lời điển hình như signnum_typical() On -0.0, +NaN, -NaN, họ trở lại 0.0, 0.0, 0.0.

int signnum_typical(double x) {
  if (x > 0.0) return 1;
  if (x < 0.0) return -1;
  return 0;
}

Thay vào đó, tôi đề xuất chức năng này: Bật -0.0, +NaN, -NaN, nó trả về -0.0, +NaN, -NaN.

double signnum_c(double x) {
  if (x > 0.0) return 1.0;
  if (x < 0.0) return -1.0;
  return x;
}

1
Ah, chính xác là những gì tôi đang theo đuổi. Điều này chỉ thay đổi trong Pharo Smalltalk github.com/pharo-project/pharo/pull/1835 và tôi tự hỏi liệu có một loại hành vi nào đó tiêu chuẩn (IEC 60559 hoặc ISO 10967) cho hành vi không âm và nan không ... Tôi thích ký hiệu javascript developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mẹo

29

Nhanh hơn các giải pháp trên, bao gồm giải pháp được đánh giá cao nhất:

(x < 0) ? -1 : (x > 0)

1
Loại x là gì? Hay bạn đang sử dụng #define?
Cơ hội

3
Kiểu của bạn không nhanh hơn. Nó sẽ gây ra lỗi nhớ cache khá thường xuyên.
Jeffrey Drake

19
Bộ nhớ cache bị mất? Tôi không biết làm thế nào. Có lẽ bạn có nghĩa là hiểu sai chi nhánh?
Catskul

2
Dường như với tôi điều này sẽ dẫn đến một cảnh báo về các loại số nguyên và boolean khó hiểu!
sergiol

Làm thế nào điều này sẽ được nhanh chóng với các chi nhánh?
Nick

16

Có một cách để làm điều đó mà không cần phân nhánh, nhưng nó không đẹp lắm.

sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));

http://graphics.stanford.edu/~seander/bithacks.html

Nhiều thứ thú vị, thông minh quá mức khác trên trang đó, quá ...


1
Nếu tôi đọc chính xác liên kết chỉ trả về -1 hoặc 0. Nếu bạn muốn -1, 0 hoặc +1 thì nó sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));hoặc sign = (v > 0) - (v < 0);.
Z boson

1
điều này ngụ ý rằng đó vlà một kiểu số nguyên không rộng hơn int
phuclv

12

Nếu tất cả những gì bạn muốn là kiểm tra dấu hiệu, hãy sử dụng Signbit (trả về true nếu đối số của nó có dấu âm). Không chắc chắn lý do tại sao bạn đặc biệt muốn trả lại -1 hoặc +1; copysign thuận tiện hơn cho việc đó, nhưng có vẻ như nó sẽ trả về +1 cho số 0 âm trên một số nền tảng chỉ hỗ trợ một phần cho số 0 âm, trong đó ký hiệu có lẽ sẽ trở về đúng.


7
Có nhiều ứng dụng toán học trong đó dấu (x) là cần thiết. Nếu không tôi sẽ làm if (x < 0).
Cơ hội

5

Nói chung, không có chức năng đăng nhập tiêu chuẩn trong C / C ++ và việc thiếu chức năng cơ bản như vậy cho bạn biết rất nhiều về các ngôn ngữ này.

Ngoài ra, tôi tin rằng cả hai quan điểm đa số về cách tiếp cận đúng để xác định hàm như vậy đều đúng và "tranh cãi" về nó thực sự là một cuộc tranh luận một khi bạn tính đến hai cảnh báo quan trọng:

  • Hàm Signum phải luôn trả về loại toán hạng của nó, tương tự như abs()hàm, bởi vì dấu hiệu thường được sử dụng để nhân với một giá trị tuyệt đối sau khi cái sau được xử lý bằng cách nào đó. Do đó, trường hợp sử dụng chính của dấu hiệu không phải là so sánh mà là số học, và trường hợp sau không nên liên quan đến bất kỳ chuyển đổi số nguyên sang / từ dấu phẩy động đắt tiền nào.

  • Các loại dấu phẩy động không có một giá trị 0 chính xác duy nhất: +0.0 có thể được hiểu là "infinitesimally trên zero" và -0.0 là "infinitesimally under zero". Đó là lý do tại sao các so sánh liên quan đến số 0 phải kiểm tra bên trong cả hai giá trị và một biểu thức như x == 0.0có thể nguy hiểm.

Về C, tôi nghĩ rằng cách tốt nhất để chuyển tiếp với các loại tích phân thực sự là sử dụng (x > 0) - (x < 0)biểu thức, vì nó nên được dịch theo kiểu không có chi nhánh và chỉ cần ba thao tác cơ bản. Xác định tốt nhất các hàm nội tuyến thực thi loại trả về khớp với loại đối số và thêm C11 define _Genericđể ánh xạ các hàm này thành một tên chung.

Với các giá trị dấu chấm động, tôi nghĩ rằng chức năng inline dựa trên C11 copysignf(1.0f, x), copysign(1.0, x)copysignl(1.0l, x)là con đường để đi, đơn giản chỉ vì chúng cũng rất có khả năng là ngành miễn phí, và bổ sung không cần đúc kết quả từ nguyên trở thành một dấu chấm động giá trị. Có lẽ bạn nên bình luận nổi bật mà hiện thực dấu chấm động lại signum sẽ không trở lại không vì đặc thù của dấu chấm động không giá trị, chế biến cân nhắc thời gian, và cũng vì nó thường là rất hữu ích trong điểm số học nổi để thu đúng -1 / + 1 dấu, thậm chí cho các giá trị bằng không.


5

Bản sao C của tôi trong một Nutshell cho thấy sự tồn tại của một chức năng tiêu chuẩn được gọi là copysign có thể hữu ích. Có vẻ như copysign (1.0, -2.0) sẽ trả về -1.0 và copysign (1.0, 2.0) sẽ trả về +1.0.

Khá gần hả?


Không chuẩn, nhưng có thể được phổ biến rộng rãi. Microsoft bắt đầu với một dấu gạch dưới, đó là quy ước họ sử dụng cho các tiện ích mở rộng không chuẩn. Tuy nhiên, không phải là sự lựa chọn tốt nhất khi bạn làm việc với các số nguyên.
Đánh dấu tiền chuộc

5
copysign đều theo tiêu chuẩn ISO C (C99) và POSIX. Xem opengroup.org/onlinepub/000095399/fifts/copysign.html
lhf

3
Những gì lhf nói. Visual Studio không phải là tài liệu tham khảo cho tiêu chuẩn C.
Stephen Canon

3

Không, nó không tồn tại trong c ++, như trong matlab. Tôi sử dụng một macro trong các chương trình của tôi cho việc này.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )

5
Bạn nên thích các mẫu hơn các macro trong C ++.
Ruslan


Tôi nghĩ rằng đây là một câu trả lời tốt sau đó tôi nhìn vào mã của riêng tôi và thấy điều này: #define sign(x) (((x) > 0) - ((x) < 0))nó cũng tốt.
Michel Rouzic

1
một hàm nội tuyến tốt hơn một macro trong C và trong mẫu C ++ thì tốt hơn
phuclv

3

Câu trả lời được chấp nhận với tình trạng quá tải dưới đây thực sự không kích hoạt giới hạn -Wtype .

template <typename T> inline constexpr
  int signum(T x, std::false_type) {
  return T(0) < x;
}

template <typename T> inline constexpr
  int signum(T x, std::true_type) {
  return (T(0) < x) - (x < T(0));
}

template <typename T> inline constexpr
  int signum(T x) {
  return signum(x, std::is_signed<T>());
}

Đối với C ++ 11, một sự thay thế có thể là.

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T const x) {
    return T(0) < x;  
}

template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T const x) {
    return (T(0) < x) - (x < T(0));  
}

Đối với tôi, nó không kích hoạt bất kỳ cảnh báo nào trên GCC 5.3.1.


Để tránh -Wunused-parametercảnh báo chỉ cần sử dụng các tham số không tên.
Jonathan Wakely

Điều đó thực sự rất đúng. Tôi đã bỏ lỡ nó. Tuy nhiên, tôi thích cách thay thế C ++ 11 hơn.
SamVanDovy

2

Bit ngoài chủ đề, nhưng tôi sử dụng điều này:

template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
    return (a > b) - (a < b);
}

template<typename T>
constexpr int sgn(const T &a) noexcept{
    return sgn(a, T(0));
}

và tôi đã tìm thấy hàm đầu tiên - hàm có hai đối số, sẽ hữu ích hơn nhiều từ sgn "tiêu chuẩn", bởi vì nó thường được sử dụng trong mã như thế này:

int comp(unsigned a, unsigned b){
   return sgn( int(a) - int(b) );
}

so với

int comp(unsigned a, unsigned b){
   return sgn(a, b);
}

không có diễn viên cho các loại không dấu và không có dấu trừ bổ sung.

thực tế tôi có đoạn mã này bằng sgn ()

template <class T>
int comp(const T &a, const T &b){
    log__("all");
    if (a < b)
        return -1;

    if (a > b)
        return +1;

    return 0;
}

inline int comp(int const a, int const b){
    log__("int");
    return a - b;
}

inline int comp(long int const a, long int const b){
    log__("long");
    return sgn(a, b);
}

1

Câu hỏi đã cũ nhưng bây giờ có loại chức năng mong muốn. Tôi đã thêm một trình bao bọc không, thay đổi trái và dec.

Bạn có thể sử dụng chức năng bao bọc dựa trên ký hiệu từ C99 để có được hành vi mong muốn chính xác (xem thêm mã bên dưới).

Trả về cho dù dấu của x là âm.
Điều này cũng có thể được áp dụng cho infinites, NaN và 0 (nếu số 0 không dấu, nó được coi là dương

#include <math.h>

int signValue(float a) {
    return ((!signbit(a)) << 1) - 1;
}

Lưu ý

Giá trị trả về
Một giá trị khác không (đúng) nếu dấu của x là âm; và không (sai) nếu không.

Sau đó, tôi nhân hai với dịch chuyển trái ("<< 1") sẽ cho chúng ta 2 cho số dương và 0 cho số âm và cuối cùng giảm 1 để lấy 1 và -1 cho các số dương và âm tương ứng theo yêu cầu của OP.


0 cũng sẽ tích cực ... điều đó có thể hoặc không thể là điều OP muốn ...
Antti Haapala

vâng, chúng ta có thể không bao giờ biết OP thực sự muốn gì nếu n = 0 ...!
Antonin GAVREL

0

Mặc dù giải pháp số nguyên trong câu trả lời được chấp nhận khá thanh lịch, nhưng điều đó làm phiền tôi rằng nó sẽ không thể trả về NAN cho loại kép, vì vậy tôi đã sửa đổi nó một chút.

template <typename T> double sgn(T val) {
    return double((T(0) < val) - (val < T(0)))/(val == val);
}

Lưu ý rằng việc trả về một dấu phẩy động NAN trái ngược với mã hóa cứng NANkhiến bit dấu được đặt trong một số triển khai , do đó, đầu ra cho val = -NANval = NANsẽ giống hệt nhau cho dù bạn có thể đặt nanđầu ra "" so với đầu ra "" -nanmột abs(val)trước khi trở về ...)



0

Đây là một triển khai thân thiện với chi nhánh:

inline int signum(const double x) {
    if(x == 0) return 0;
    return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

Trừ khi dữ liệu của bạn có số không bằng một nửa số, ở đây, bộ dự đoán nhánh sẽ chọn một trong những nhánh là phổ biến nhất. Cả hai chi nhánh chỉ liên quan đến các hoạt động đơn giản.

Ngoài ra, trên một số trình biên dịch và kiến ​​trúc CPU, một phiên bản hoàn toàn không phân nhánh có thể nhanh hơn:

inline int signum(const double x) {
    return (x != 0) * 
        (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}

Điều này hoạt động cho định dạng dấu phẩy động nhị phân chính xác kép của IEEE 754: binary64 .


-1
int sign(float n)
{     
  union { float f; std::uint32_t i; } u { n };
  return 1 - ((u.i >> 31) << 1);
}

Hàm này giả định:

  • biểu diễn binary32 của số dấu phẩy động
  • một trình biên dịch tạo một ngoại lệ về quy tắc bí danh nghiêm ngặt khi sử dụng một liên minh có tên

3
Vẫn còn một số giả định xấu ở đây. Ví dụ, tôi không tin rằng tuổi thọ của số float được đảm bảo là độ bền của số nguyên. Kiểm tra của bạn cũng thất bại trên bất kỳ kiến ​​trúc nào sử dụng ILP64. Thực sự, bạn chỉ đang thực hiện lại copysign; nếu bạn đang sử dụng, static_assertbạn đã có C ++ 11 và cũng có thể thực sự sử dụng copysign.


-3

Tại sao nên sử dụng toán tử ternary và if-other khi bạn có thể đơn giản làm điều này

#define sgn(x) x==0 ? 0 : x/abs(x)

3
Định nghĩa của bạn sử dụng một toán tử ternary là tốt.
Martin R

Có Chắc chắn, nhưng nó chỉ sử dụng một toán tử ternary để phân tách các số 0 và khác không. Các phiên bản khác bao gồm các ops ternary lồng nhau để phân tách dương, âm và 0.
Jagreet

Sử dụng phép chia số nguyên rất không hiệu quả và abs () chỉ dành cho số nguyên.
Michel Rouzic

Hành vi không xác định có thể khi x == INT_MIN.
chux - Tái lập lại
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.