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.
x==0
. Menurut IEEE 754 , nol negatif dan nol positif harus sama.Jawaban:
Terkejut belum ada yang diposting versi C ++ tipe-safe:
Manfaat:
copysign
lambat, terutama jika Anda perlu mempromosikan dan kemudian mempersempit lagi. Ini tanpa cabang dan mengoptimalkan dengan sangat baikPeringatan:
Bagian
< 0
dari cek memicu-Wtype-limits
peringatan GCC ketika dipakai untuk tipe yang tidak ditandatangani. Anda dapat menghindari ini dengan menggunakan beberapa kelebihan:(Yang merupakan contoh yang baik dari peringatan pertama.)
sumber
std::copysign
tampaknya 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) ...copysign
int, 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.Saya tidak tahu fungsi standar untuk itu. Inilah cara yang menarik untuk menulisnya:
Berikut cara yang lebih mudah dibaca untuk melakukannya:
Jika Anda menyukai operator ternary, Anda dapat melakukan ini:
sumber
x==0
.<
,>
... akan menghasilkan 1 jika hubungan yang ditentukan benar dan 0 jika itu salah"0
is "false"; nilai lainnya adalah "benar"; namun, operator relasional dan kesetaraan selalu kembali0
atau1
(lihat Standar 6.5.8 dan 6.5.9). - nilai ekspresia * (x == 42)
adalah0
ataua
.copysign
integralx
bahkan jika saya memilikinya tersedia.Ada fungsi perpustakaan matematika C99 yang disebut copysign (), yang mengambil tanda dari satu argumen dan nilai absolut dari yang lain:
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.
sumber
Tampaknya sebagian besar jawaban tidak menjawab pertanyaan awal.
Tidak di perpustakaan standar, namun ada
copysign
yang dapat digunakan dengan cara yang hampir samacopysign(1.0, arg)
dan ada fungsi tanda yang benarboost
, yang mungkin juga menjadi bagian dari standar.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
sumber
Rupanya, jawaban untuk pertanyaan poster asli adalah tidak. Tidak ada fungsi standar C ++
sgn
.sumber
copysign()
tidak akan membuat parameter pertama Anda 0,0 jika yang kedua adalah 0,0. Dengan kata lain, John benar.Ya, tergantung definisi.
C99 dan yang lebih baru memiliki
signbit()
makro di<math.h>
Namun OP menginginkan sesuatu yang sedikit berbeda.
Lebih dalam:
Posting adalah tidak spesifik dalam kasus berikut:
x = 0.0, -0.0, +NaN, -NaN
.Sebuah klasik
signum()
kembali+1
padax>0
,-1
padax<0
dan0
dix==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 kembali0.0, 0.0, 0.0
.Sebagai gantinya, saya mengusulkan fungsi ini: Hidup
-0.0, +NaN, -NaN
, itu kembali-0.0, +NaN, -NaN
.sumber
Lebih cepat daripada solusi di atas, termasuk yang berperingkat tertinggi:
sumber
Ada cara untuk melakukannya tanpa bercabang, tapi itu tidak terlalu cantik.
http://graphics.stanford.edu/~seander/bithacks.html
Banyak hal menarik lainnya, terlalu pintar di halaman itu, juga ...
sumber
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
atausign = (v > 0) - (v < 0);
.v
ini adalah tipe integer yang tidak lebih lebar dari intJika 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.
sumber
if (x < 0)
.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.0
bisa 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 C11define _Generic
untuk 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)
dancopysignl(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.sumber
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?
sumber
Tidak, itu tidak ada di c ++, seperti di matlab. Saya menggunakan makro dalam program saya untuk ini.
sumber
#define sign(x) (((x) > 0) - ((x) < 0))
yang juga bagus.Jawaban yang diterima dengan kelebihan di bawah ini memang tidak memicu -Wtype-limit .
Untuk C ++ 11 alternatif bisa jadi.
Bagi saya itu tidak memicu peringatan pada GCC 5.3.1.
sumber
-Wunused-parameter
peringatan, gunakan saja parameter yang tidak disebutkan namanya.Agak di luar topik, tapi saya menggunakan ini:
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:
vs.
tidak ada pemeran untuk tipe yang tidak ditandatangani dan tidak ada minus tambahan.
sebenarnya saya memiliki potongan kode ini menggunakan sgn ()
sumber
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).
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:
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.
sumber
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.
Perhatikan bahwa mengembalikan floating point NAN sebagai lawan dari kode keras
NAN
menyebabkan bit tanda diatur dalam beberapa implementasi , sehingga output untukval = -NAN
danval = NAN
akan identik tidak peduli apa (jika Anda lebih memilihnan
output " " daripada-nan
Anda dapat menempatkan sebuahabs(val)
sebelum kembalinya ...)sumber
Anda dapat menggunakan
boost::math::sign()
metode dariboost/math/special_functions/sign.hpp
jika dorongan tersedia.sumber
Berikut ini adalah implementasi yang ramah cabang:
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:
Ini berfungsi untuk format floating-point biner IEEE 754 presisi ganda: binary64 .
sumber
Fungsi ini mengasumsikan:
sumber
copysign
; jika Anda menggunakanstatic_assert
C ++ 11, dan mungkin juga benar-benar digunakancopysign
.sumber
Mengapa menggunakan operator ternary dan jika-lain saat Anda bisa melakukan ini
sumber
x == INT_MIN
.