Apakah ada fungsi tanda standar (signum, sgn) di C / C ++?

409

Saya ingin fungsi yang mengembalikan -1 untuk angka negatif dan +1 untuk angka positif. http://en.wikipedia.org/wiki/Sign_function Cukup mudah untuk menulis sendiri, tetapi sepertinya sesuatu yang seharusnya ada di perpustakaan standar di suatu tempat.

Sunting: Secara khusus, saya sedang mencari fungsi yang bekerja pada floats.

amuk
sumber
13
Apa yang harus dikembalikan untuk 0?
Craig McQueen
61
@Craig McQueen; itu tergantung pada apakah itu nol positif atau nol negatif.
ysth
1
Saya perhatikan bahwa Anda menetapkan nilai kembali sebagai bilangan bulat. Apakah Anda mencari solusi yang menggunakan bilangan bulat atau angka floating point?
Mark Byers
6
@ysth @Craig McQueen, false for floats juga, bukan? Definisi sgn (x) mengatakan untuk mengembalikan 0 jika x==0. Menurut IEEE 754 , nol negatif dan nol positif harus sama.
RJFalconer
5
@ysth "itu tergantung pada nol positif atau nol negatif". Sebenarnya tidak.
RJFalconer

Jawaban:

506

Terkejut belum ada yang diposting versi C ++ tipe-safe:

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

Manfaat:

  • Sebenarnya mengimplementasikan signum (-1, 0, atau 1). Implementasi di sini menggunakan copysign hanya mengembalikan -1 atau 1, yang tidak masuk. Juga, beberapa implementasi di sini mengembalikan float (atau T) daripada sebuah int, yang tampaknya boros.
  • Dapat digunakan untuk int, float, dobel, celana pendek yang tidak ditandatangani, atau jenis kustom apa pun yang dapat dibangun dari integer 0 dan dapat dipesan.
  • Cepat! copysignlambat, terutama jika Anda perlu mempromosikan dan kemudian mempersempit lagi. Ini tanpa cabang dan mengoptimalkan dengan sangat baik
  • Sesuai standar! Retasan bithift rapi, tetapi hanya berfungsi untuk beberapa representasi bit, dan tidak berfungsi ketika Anda memiliki tipe yang tidak ditandai. Ini dapat diberikan sebagai spesialisasi manual bila perlu.
  • Tepat! Perbandingan sederhana dengan nol dapat mempertahankan representasi presisi tinggi internal mesin (mis. 80 bit pada x87), dan menghindari putaran prematur ke nol.

Peringatan:

  • Ini templat sehingga mungkin perlu waktu lebih lama untuk dikompilasi dalam beberapa keadaan.
  • Rupanya beberapa orang berpikir penggunaan fungsi perpustakaan standar baru, agak esoterik, dan sangat lambat yang bahkan tidak benar-benar menerapkan signum lebih dapat dimengerti.
  • Bagian < 0dari cek memicu -Wtype-limitsperingatan GCC ketika dipakai untuk tipe yang tidak ditandatangani. Anda dapat menghindari ini dengan menggunakan beberapa kelebihan:

    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>());
    }

    (Yang merupakan contoh yang baik dari peringatan pertama.)

Pharap
sumber
18
@ GM: GCC baru saja (4,5) berhenti memiliki kuadrat biaya untuk jumlah instantiations untuk fungsi template, dan mereka masih secara drastis lebih mahal untuk mem-parsing dan membuat instantiate daripada fungsi yang ditulis secara manual atau preprocessor C standar. Linker juga harus melakukan lebih banyak pekerjaan untuk menghapus instance duplikat. Template juga mendorong # include-in-# include, yang membuat perhitungan dependensi membutuhkan waktu lebih lama dan perubahan kecil (sering kali implementasi, bukan antarmuka) untuk memaksa lebih banyak file untuk dikompilasi ulang.
15
@ Jo: Ya, dan masih belum ada biaya yang jelas. C ++ menggunakan templat, itu hanya sesuatu yang kita semua harus mengerti, menerima, dan menyelesaikannya.
GManNickG
42
Tunggu, apa ini bisnis "copysign lambat" ...? Menggunakan kompiler saat ini (g ++ 4.6+, dentang ++ 3.0), std::copysigntampaknya menghasilkan kode yang sangat baik bagi saya: 4 instruksi (inlined), tanpa percabangan, seluruhnya menggunakan FPU. Sebaliknya, resep yang diberikan dalam jawaban ini menghasilkan kode yang jauh lebih buruk (lebih banyak instruksi, termasuk multiply, bergerak bolak-balik antara unit integer dan FPU) ...
snogglethorpe
14
@ snogglethorpe: Jika Anda memanggil copysignint, ia mempromosikan float / double, dan harus menyempit kembali saat kembali. Kompiler Anda dapat mengoptimalkan promosi itu tetapi saya tidak dapat menemukan saran yang dijamin oleh standar. Juga untuk mengimplementasikan signum melalui copysign, Anda perlu menangani case 0 secara manual - pastikan Anda menyertakannya dalam perbandingan kinerja apa pun.
53
Versi pertama bukan tanpa cabang. Mengapa orang berpikir bahwa perbandingan yang digunakan dalam ekspresi tidak akan menghasilkan cabang? Itu akan di sebagian besar arsitektur. Hanya prosesor yang memiliki cmove (atau predikasi) yang akan menghasilkan kode branchless, tetapi mereka akan melakukannya juga untuk terner atau jika / jika itu adalah kemenangan.
Patrick Schlüter
271

Saya tidak tahu fungsi standar untuk itu. Inilah cara yang menarik untuk menulisnya:

(x > 0) - (x < 0)

Berikut cara yang lebih mudah dibaca untuk melakukannya:

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

Jika Anda menyukai operator ternary, Anda dapat melakukan ini:

(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Mark Byers
sumber
7
Tandai tebusan, ekspresi Anda memberikan hasil yang salah untuk x==0.
avakar
3
@Svante: "Masing-masing operator <, >... akan menghasilkan 1 jika hubungan yang ditentukan benar dan 0 jika itu salah"
Stephen Canon
11
@ Vincent: tidak persis. Nilai 0is "false"; nilai lainnya adalah "benar"; namun, operator relasional dan kesetaraan selalu kembali 0atau 1(lihat Standar 6.5.8 dan 6.5.9). - nilai ekspresi a * (x == 42)adalah 0atau a.
pmg
21
Tanda Berkinerja Tinggi, saya kagum bahwa Anda melewatkan tag C ++. Jawaban ini sangat valid dan tidak layak untuk di-voting. Selain itu, saya tidak akan menggunakan copysignintegral xbahkan jika saya memilikinya tersedia.
avakar
6
Adakah yang benar-benar memeriksa kode GCC / G ++ / kompiler apa yang dipancarkan pada platform nyata? Dugaan saya adalah bahwa versi "tanpa cabang" menggunakan dua cabang bukan satu. Bitshifting mungkin jauh lebih cepat - dan lebih portabel dalam hal kinerja.
Jørgen Fogh
192

Ada fungsi perpustakaan matematika C99 yang disebut copysign (), yang mengambil tanda dari satu argumen dan nilai absolut dari yang lain:

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

akan memberi Anda hasil +/- 1.0, tergantung pada tanda nilai. Perhatikan bahwa nol floating point ditandatangani: (+0) akan menghasilkan +1, dan (-0) akan menghasilkan -1.

datang badai
sumber
57
Terpilih yang satu ini, jawaban yang paling populer downvoted. Kiri terhuyung-huyung dengan kagum bahwa komunitas SO tampaknya lebih suka hack untuk menggunakan fungsi perpustakaan standar. Semoga dewa pemrograman mengutuk Anda semua untuk mencoba menguraikan hack yang digunakan oleh programmer pintar yang tidak terbiasa dengan standar bahasa. Ya, saya tahu ini akan menghabiskan biaya satu ton rep pada SO, tapi saya lebih suka berpihak pada badai daripada kalian semua ...
Mark Kinerja Tinggi
34
Ini dekat, tetapi memberikan jawaban yang salah untuk nol (menurut artikel Wikipedia setidaknya dalam pertanyaan). Saran yang bagus. Tetap memberi +1.
Mark Byers
4
Jika Anda menginginkan bilangan bulat, atau jika Anda ingin hasil signum yang tepat untuk nol, saya suka jawaban Mark Byers, yang sangat elegan! Jika Anda tidak peduli dengan hal di atas, copysign () mungkin memiliki peningkatan kinerja, tergantung pada aplikasi - jika saya mengoptimalkan loop kritis, saya akan mencoba keduanya.
comingstorm
10
1) C99 tidak sepenuhnya didukung di mana-mana (pertimbangkan VC ++); 2) ini juga merupakan pertanyaan C ++. Ini adalah jawaban yang bagus, tetapi jawaban yang dipilih juga berfungsi, dan lebih dapat diterapkan secara luas.
Pavel Minaev
5
Penyelamat! Diperlukan cara untuk menentukan antara -0.0 dan 0.0
Ólafur Waage
79

Tampaknya sebagian besar jawaban tidak menjawab pertanyaan awal.

Apakah ada fungsi tanda standar (signum, sgn) di C / C ++?

Tidak di perpustakaan standar, namun ada copysignyang dapat digunakan dengan cara yang hampir sama copysign(1.0, arg)dan ada fungsi tanda yang benar boost, yang mungkin juga menjadi bagian dari standar.

    #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_functions.html

Catskul
sumber
5
Ini harus menjadi jawaban yang paling banyak dipilih, karena memberikan solusi terdekat dengan apa yang ditanyakan dalam pertanyaan.
BartoszKP
Saya telah bertanya-tanya selama beberapa menit terakhir mengapa perpustakaan standar tidak memiliki fungsi tanda. Ini sangat umum - pasti lebih umum digunakan daripada fungsi gamma yang dapat ditemukan di header cmath.
Taozi
4
Penjelasan yang sering saya dapatkan untuk pertanyaan serupa adalah "cukup mudah untuk menerapkan sendiri" IMO mana yang bukan alasan yang bagus. Ini benar-benar memungkiri masalah di mana standarisasi, kasus tepi tidak jelas, dan di mana harus meletakkan alat yang banyak digunakan.
Catskul
77

Rupanya, jawaban untuk pertanyaan poster asli adalah tidak. Tidak ada fungsi standar C ++ sgn.

John
sumber
2
@ SR Anda tidak benar. copysign()tidak akan membuat parameter pertama Anda 0,0 jika yang kedua adalah 0,0. Dengan kata lain, John benar.
Alexis Wilke
30

Apakah ada fungsi tanda standar (signum, sgn) di C / C ++?

Ya, tergantung definisi.

C99 dan yang lebih baru memiliki signbit()makro di<math.h>

int signbit(mengambang nyata x);
The signbitpengembalian makro nilai nol jika dan hanya jika tanda nilai argumen negatif. C11 §7.12.3.6


Namun OP menginginkan sesuatu yang sedikit berbeda.

Saya ingin fungsi yang mengembalikan -1 untuk angka negatif dan +1 untuk angka positif. ... sebuah fungsi yang bekerja pada pelampung.

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

Lebih dalam:

Posting adalah tidak spesifik dalam kasus berikut: x = 0.0, -0.0, +NaN, -NaN.

Sebuah klasik signum()kembali +1pada x>0, -1pada x<0dan 0di x==0.

Banyak jawaban telah membahas hal itu, tetapi tidak membahasnya x = -0.0, +NaN, -NaN. Banyak diarahkan untuk sudut pandang bilangan bulat yang biasanya tidak memiliki Not-a-Numbers ( NaN ) dan -0.0 .

Fungsi jawaban umum seperti signnum_typical() Hidup -0.0, +NaN, -NaN, mereka kembali 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;
}

Sebagai gantinya, saya mengusulkan fungsi ini: Hidup -0.0, +NaN, -NaN, itu kembali -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;
}
chux - Pasang kembali Monica
sumber
1
Ah, persis apa yang saya kejar. Ini baru saja berubah di Pharo Smalltalk github.com/pharo-project/pharo/pull/1835 dan saya bertanya-tanya apakah ada semacam standar (IEC 60559 atau ISO 10967) yang menentukan perilaku untuk nol negatif dan perilaku nan ... Saya suka javascript sign developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
aka.nice
29

Lebih cepat daripada solusi di atas, termasuk yang berperingkat tertinggi:

(x < 0) ? -1 : (x > 0)
xnx
sumber
1
Apa tipe x? Atau apakah Anda menggunakan #define?
Peluang
3
Tipe Anda tidak lebih cepat. Ini akan menyebabkan cache cukup sering hilang.
Jeffrey Drake
19
Cache miss? Saya tidak yakin bagaimana caranya. Mungkin maksud Anda salah duga cabang?
Catskul
2
Menurut saya ini akan menghasilkan peringatan tipe integer dan boolean yang membingungkan!
sergiol
bagaimana ini akan cepat dengan cabang?
Nick
16

Ada cara untuk melakukannya tanpa bercabang, tapi itu tidak terlalu cantik.

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

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

Banyak hal menarik lainnya, terlalu pintar di halaman itu, juga ...

Tim Sylvester
sumber
1
Jika saya membaca tautan dengan benar yang hanya menghasilkan -1 atau 0. Jika Anda ingin -1, 0, atau +1 maka itu sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));atau sign = (v > 0) - (v < 0);.
Z boson
1
ini menyiratkan bahwa vini adalah tipe integer yang tidak lebih lebar dari int
phuclv
12

Jika semua yang Anda inginkan adalah menguji tanda, gunakan signbit (mengembalikan true jika argumennya memiliki tanda negatif). Tidak yakin mengapa Anda ingin -1 atau +1 dikembalikan; copysign lebih nyaman untuk itu, tetapi sepertinya itu akan mengembalikan +1 untuk nol negatif pada beberapa platform dengan hanya dukungan parsial untuk nol negatif, di mana signbit mungkin akan mengembalikan true.

ysth
sumber
7
Ada banyak aplikasi matematika di mana tanda (x) diperlukan. Kalau tidak, aku akan melakukannya if (x < 0).
Peluang
5

Secara umum, tidak ada fungsi signum standar di C / C ++, dan kurangnya fungsi fundamental seperti itu memberi tahu Anda banyak tentang bahasa-bahasa ini.

Terlepas dari itu, saya percaya kedua sudut pandang mayoritas tentang pendekatan yang tepat untuk mendefinisikan fungsi seperti itu dengan cara yang benar, dan "kontroversi" tentang itu sebenarnya bukan argumen ketika Anda mempertimbangkan dua peringatan penting:

  • Sebuah signum fungsi harus selalu kembali jenis operan nya, mirip dengan sebuah abs()fungsi, karena signum biasanya digunakan untuk perkalian dengan nilai absolut setelah yang terakhir telah diproses entah bagaimana. Oleh karena itu, kasus penggunaan utama signum bukan perbandingan tetapi aritmatika, dan yang terakhir tidak boleh melibatkan konversi bilangan bulat ke / dari-floating-point yang mahal.

  • Tipe floating point tidak menampilkan nilai nol tepat tunggal: +0.0 dapat diartikan sebagai "jauh di atas nol", dan -0,0 sebagai "sangat jauh di bawah nol". Itulah alasan mengapa perbandingan yang melibatkan nol harus secara internal memeriksa kedua nilai, dan ekspresi seperti itu x == 0.0bisa berbahaya.

Mengenai C, saya pikir cara terbaik untuk maju dengan tipe integral adalah dengan menggunakan (x > 0) - (x < 0)ekspresi, karena harus diterjemahkan dalam mode bebas cabang, dan hanya membutuhkan tiga operasi dasar. Mendefinisikan fungsi inline terbaik yang memberlakukan tipe kembali yang cocok dengan tipe argumen, dan menambahkan C11 define _Genericuntuk memetakan fungsi-fungsi ini ke nama umum.

Dengan nilai floating point, saya pikir fungsi inline berdasarkan C11 copysignf(1.0f, x),, copysign(1.0, x)dan copysignl(1.0l, x)adalah cara untuk pergi, hanya karena mereka juga sangat mungkin menjadi bebas cabang, dan juga tidak memerlukan casting hasil dari integer kembali ke floating point nilai. Anda mungkin harus berkomentar dengan jelas bahwa implementasi signum titik mengambang Anda tidak akan mengembalikan nol karena kekhasan nilai nol titik mengambang, pertimbangan waktu pemrosesan, dan juga karena sering sangat berguna dalam aritmatika titik apung untuk menerima -1 / + yang benar 1 tanda, bahkan untuk nilai nol.

Tabernakel
sumber
5

Salinan C saya dalam Singkatnya mengungkapkan adanya fungsi standar yang disebut copysign yang mungkin berguna. Sepertinya copysign (1.0, -2.0) akan mengembalikan -1.0 dan copysign (1.0, 2.0) akan mengembalikan +1.0.

Cukup dekat ya?

Tanda Kinerja Tinggi
sumber
Tidak standar, tetapi mungkin tersedia secara luas. Microsoft memulai dengan garis bawah, yang merupakan konvensi yang mereka gunakan untuk ekstensi non-standar. Bukan pilihan terbaik saat Anda bekerja dengan bilangan bulat.
Mark Ransom
5
copysign keduanya dalam standar ISO C (C99) dan POSIX. Lihat opengroup.org/onlinepubs/000095399/functions/copysign.html
lhf
3
Apa yang dikatakan. Visual Studio bukan referensi untuk standar C.
Stephen Canon
3

Tidak, itu tidak ada di c ++, seperti di matlab. Saya menggunakan makro dalam program saya untuk ini.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
mengobrol
sumber
5
Orang harus lebih suka template daripada makro di C ++.
Ruslan
Di C, tidak ada template ...... helloacm.com/how-to-implement-the-sgn-function-in-c
doctorlai
Saya pikir ini adalah jawaban yang bagus kemudian saya melihat kode saya sendiri dan menemukan ini: #define sign(x) (((x) > 0) - ((x) < 0))yang juga bagus.
Michel Rouzic
1
fungsi inline lebih baik daripada makro di C, dan dalam template C ++ lebih baik
phuclv
3

Jawaban yang diterima dengan kelebihan di bawah ini memang tidak memicu -Wtype-limit .

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>());
}

Untuk C ++ 11 alternatif bisa jadi.

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));  
}

Bagi saya itu tidak memicu peringatan pada GCC 5.3.1.

SamVanDonut
sumber
Untuk menghindari -Wunused-parameterperingatan, gunakan saja parameter yang tidak disebutkan namanya.
Jonathan Wakely
Itu sebenarnya sangat benar. Saya melewatkan itu. Namun, saya lebih suka alternatif C ++ 11.
SamVanDonut
2

Agak di luar topik, tapi saya menggunakan ini:

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));
}

dan saya menemukan fungsi pertama - satu dengan dua argumen, untuk menjadi jauh lebih bermanfaat dari "standar" sgn (), karena paling sering digunakan dalam kode seperti ini:

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

vs.

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

tidak ada pemeran untuk tipe yang tidak ditandatangani dan tidak ada minus tambahan.

sebenarnya saya memiliki potongan kode ini menggunakan 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);
}
Nick
sumber
1

Pertanyaannya sudah tua tetapi sekarang ada fungsi yang diinginkan. Saya menambahkan pembungkus dengan tidak, shift kiri dan Desember.

Anda dapat menggunakan fungsi pembungkus berdasarkan signbit dari C99 untuk mendapatkan perilaku yang diinginkan yang tepat (lihat kode lebih lanjut di bawah).

Mengembalikan apakah tanda x negatif.
Ini dapat juga diterapkan pada infinites, NaNs dan nol (jika nol tidak ditandatangani, itu dianggap positif

#include <math.h>

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

NB: Saya menggunakan operand not ("!") Karena nilai kembalian dari signbit tidak ditentukan menjadi 1 (meskipun contohnya mari kita berpikir akan selalu seperti ini) tetapi berlaku untuk angka negatif:

Nilai kembali Nilai
bukan nol (benar) jika tanda x negatif; dan nol (salah) jika tidak.

Lalu saya kalikan dua dengan shift kiri ("<< 1") yang akan memberi kita 2 untuk angka positif dan 0 untuk yang negatif dan akhirnya dikurangi dengan 1 untuk mendapatkan 1 dan -1 untuk masing-masing angka positif dan negatif seperti yang diminta oleh OP.

Antonin GAVREL
sumber
0 akan menjadi positif juga ... yang mungkin atau mungkin tidak diinginkan OP ...
Antti Haapala
nah kita mungkin tidak pernah tahu apa yang benar-benar diinginkan OP jika n = 0 ...!
Antonin GAVREL
0

Sementara solusi integer dalam jawaban yang diterima cukup elegan, itu mengganggu saya bahwa itu tidak akan dapat mengembalikan NAN untuk tipe ganda, jadi saya memodifikasinya sedikit.

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

Perhatikan bahwa mengembalikan floating point NAN sebagai lawan dari kode keras NANmenyebabkan bit tanda diatur dalam beberapa implementasi , sehingga output untuk val = -NANdan val = NANakan identik tidak peduli apa (jika Anda lebih memilih nanoutput " " daripada -nanAnda dapat menempatkan sebuah abs(val)sebelum kembalinya ...)

mrclng
sumber
0

Anda dapat menggunakan boost::math::sign()metode dari boost/math/special_functions/sign.hppjika dorongan tersedia.

khkarens
sumber
Perhatikan bahwa ini disarankan sebelumnya: stackoverflow.com/a/16869019/1187415 .
Martin R
0

Berikut ini adalah implementasi yang ramah cabang:

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

Kecuali jika data Anda memiliki nol sebagai setengah dari angka, di sini prediktor cabang akan memilih salah satu cabang sebagai yang paling umum. Kedua cabang hanya melibatkan operasi sederhana.

Atau, pada beberapa kompiler dan arsitektur CPU versi yang benar-benar tanpa cabang mungkin lebih cepat:

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

Ini berfungsi untuk format floating-point biner IEEE 754 presisi ganda: binary64 .

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

Fungsi ini mengasumsikan:

  • binary32 representasi angka floating point
  • kompiler yang membuat pengecualian tentang aturan aliasing yang ketat saat menggunakan serikat bernama
Gigi
sumber
3
Masih ada beberapa asumsi buruk di sini. Misalnya saya tidak percaya endianness dari float dijamin sebagai endianness dari integer. Pemeriksaan Anda juga gagal pada arsitektur apa pun yang menggunakan ILP64. Sungguh, Anda hanya mengimplementasikan ulang copysign; jika Anda menggunakan static_assertC ++ 11, dan mungkin juga benar-benar digunakan copysign.
-3
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
cyberion
sumber
-3

Mengapa menggunakan operator ternary dan jika-lain saat Anda bisa melakukan ini

#define sgn(x) x==0 ? 0 : x/abs(x)
Jagreet
sumber
3
Definisi Anda menggunakan operator ternary juga.
Martin R
Ya Jelas, tetapi hanya menggunakan satu operator ternary untuk memisahkan angka nol dan bukan nol. Versi lain termasuk ops ternary bersarang untuk memisahkan positif, negatif dan nol.
Jagreet
Menggunakan divisi integer sangat tidak efisien dan abs () hanya untuk integer.
Michel Rouzic
Perilaku yang tidak terdefinisi dapat terjadi saat x == INT_MIN.
chux - Reinstate Monica