Saya membutuhkan fungsi pembulatan titik mengambang sederhana, dengan demikian:
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
Saya dapat menemukan ceil()
dan floor()
dalam math.h - tetapi tidak round()
.
Apakah ada di pustaka C ++ standar dengan nama lain, atau hilang?
c++
floating-point
rounding
Roddy
sumber
sumber
std::cout << std::fixed << std::setprecision(0) << -0.9
, misalnya.round
tersedia sejak C ++ 11 in<cmath>
. Sayangnya, jika Anda menggunakan Microsoft Visual Studio, perangkat ini masih hilang: connect.microsoft.com/VisualStudio/feedback/details/775474/...round
memiliki banyak peringatan. Sebelum C ++ 11, standar mengandalkan C90 yang tidak termasukround
. C ++ 11 bergantung pada C99 yang memang memilikiround
tetapi juga seperti yang saya catat termasuktrunc
yang memiliki sifat berbeda dan mungkin lebih tepat tergantung pada aplikasinya. Sebagian besar jawaban juga tampaknya mengabaikan bahwa pengguna mungkin ingin mengembalikan tipe integral yang memiliki lebih banyak masalah.Jawaban:
Tidak ada putaran () di pustaka standar C ++ 98. Anda bisa menulis sendiri. Berikut ini adalah implementasi round-half-up :
Alasan yang mungkin tidak ada fungsi bundar di pustaka standar C ++ 98 adalah bahwa itu sebenarnya dapat diimplementasikan dengan cara yang berbeda. Di atas adalah salah satu cara umum tetapi ada yang lain seperti round-to-even , yang kurang bias dan umumnya lebih baik jika Anda akan melakukan banyak pembulatan; itu sedikit lebih kompleks untuk diimplementasikan.
sumber
Boost menawarkan serangkaian fungsi pembulatan sederhana.
Untuk informasi lebih lanjut, lihat dokumentasi Boost .
Mengedit : Sejak C ++ 11, ada
std::round
,std::lround
danstd::llround
.sumber
floor(value + 0.5)
pendekatan naif !floor(value + 0.5)
.floor(value + 0.5)
itu sama sekali tidak naif, tetapi lebih tergantung pada konteks dan sifatnya nilai yang ingin Anda bulatkan!Standar C ++ 03 bergantung pada standar C90 untuk apa yang standar sebut Perpustakaan C Standar yang tercakup dalam konsep standar C ++ 03 ( standar konsep terdekat yang tersedia untuk publik dengan C ++ 03 adalah N1804 ) bagian
1.2
Referensi normatif :Jika kita pergi ke dokumentasi C untuk round, lround, llround pada cppreference kita dapat melihat bahwa round dan fungsi terkait adalah bagian dari C99 dan karenanya tidak akan tersedia di C ++ 03 atau sebelumnya.
Dalam C ++ 11 perubahan ini karena C ++ 11 bergantung pada standar draft C99 untuk pustaka standar C dan karenanya menyediakan std :: round dan untuk tipe pengembalian integral std :: lround, std :: llround :
Pilihan lain juga dari C99 adalah std :: trunc yang:
Jika Anda perlu mendukung aplikasi non C ++ 11, taruhan terbaik Anda adalah menggunakan boost round, iround, lround, llround atau boost trunc .
Memutar versi putaran Anda sendiri itu sulit
Menggulung sendiri mungkin tidak sepadan dengan usaha yang lebih sulit daripada yang terlihat: membulatkan float ke integer terdekat, bagian 1 , Rounding float ke integer terdekat, bagian 2 dan Rounding float ke integer terdekat, bagian 3 menjelaskan:
Misalnya, roll yang umum digunakan
std::floor
dan ditambahkan oleh implementasi Anda0.5
tidak berfungsi untuk semua input:Satu input yang gagal adalah
0.49999999999999994
, ( lihat langsung ).Implementasi umum lainnya melibatkan casting tipe floating point ke tipe integral, yang dapat memicu perilaku tidak terdefinisi dalam kasus di mana bagian integral tidak dapat diwakili dalam tipe tujuan. Kita dapat melihat ini dari konsep C ++ bagian standar
4.9
Konversi integral-mengambang yang mengatakan ( penekanan milikku ):Sebagai contoh:
Mengingat
std::numeric_limits<unsigned int>::max()
adalah4294967295
maka panggilan berikut:akan menyebabkan overflow, ( lihat langsung ).
Kita dapat melihat betapa sulitnya ini sebenarnya dengan melihat jawaban ini untuk cara ringkas untuk mengimplementasikan round () di C? yang mereferensikan versi newlibs dari putaran float presisi tunggal. Ini adalah fungsi yang sangat panjang untuk sesuatu yang tampaknya sederhana. Tampaknya tidak mungkin bahwa siapa pun tanpa pengetahuan mendalam tentang implementasi floating point dapat dengan benar mengimplementasikan fungsi ini:
Di sisi lain, jika tidak ada solusi lain yang dapat digunakan, newlib berpotensi menjadi pilihan karena merupakan implementasi yang teruji dengan baik.
sumber
round(-0.0)
. C spec tampaknya tidak menentukan. Saya harapkan-0.0
sebagai hasilnya.std::rint()
sering lebih disukaistd::round()
ketika C ++ 11 tersedia karena alasan numerik dan kinerja. Ini menggunakan mode pembulatan saat ini, tidak sepertiround()
mode khusus. Ini bisa jauh lebih efisien pada x86, di manarint
bisa sejajar dengan satu instruksi. (gcc dan dentang melakukan itu bahkan tanpa-ffast-math
godbolt.org/g/5UsL2e , sementara hanya dentang inline yang hampir setaranearbyint()
) ARM memiliki dukungan instruksi tunggal untukround()
, tetapi pada x86 hanya dapat sejalan dengan beberapa instruksi, dan hanya dengan-ffast-math
Mungkin perlu dicatat bahwa jika Anda menginginkan hasil bilangan bulat dari pembulatan, Anda tidak perlu melewati langit-langit atau lantai. Yaitu,
sumber
Ini tersedia sejak C ++ 11 dalam cmath (menurut http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )
Keluaran:
sumber
lround
danllround
untuk hasil yang tidak terpisahkanlrint
untuk menggunakan mode pembulatan saat ini daripadaround
tiebreak funky away-from-zero.Biasanya diimplementasikan sebagai
floor(value + 0.5)
.Sunting: dan itu mungkin tidak disebut putaran karena setidaknya ada tiga algoritma pembulatan yang saya tahu: bulat ke nol, bulat ke bilangan bulat terdekat, dan pembulatan bankir. Anda meminta bilangan bulat ke bilangan bulat terdekat.
sumber
Ada 2 masalah yang kita lihat:
Konversi pembulatan berarti pembulatan ± float / dobel ke lantai / ceil float / dobel terdekat. Mungkin masalah Anda berakhir di sini. Tetapi jika Anda diharapkan untuk mengembalikan Int / Long, Anda perlu melakukan konversi tipe, dan dengan demikian masalah "Overflow" mungkin mengenai solusi Anda. SO, lakukan pemeriksaan untuk kesalahan dalam fungsi Anda
dari: http://www.cs.tut.fi/~jkorpela/round.html
sumber
LONG_MIN-0.5
danLONG_MAX+0.5
memperkenalkan komplikasi karena matematika mungkin tidak tepat.LONG_MAX
dapat melebihidouble
presisi untuk konversi yang tepat. Lebih lanjut kemungkinan inginassert(x < LONG_MAX+0.5);
(<vs <=) karenaLONG_MAX+0.5
mungkin benar-benar dapat diwakili dan(x)+0.5
mungkin memiliki hasil yang pastiLONG_MAX+1
yang gagallong
dilemparkan. Masalah sudut lainnya juga.round(double)
, sudah ada fungsi perpustakaan matematika standar dari nama itu (dalam C ++ 11) sehingga membingungkan. Gunakanstd::lrint(x)
jika tersedia.Jenis pembulatan tertentu juga diterapkan dalam Peningkatan:
Perhatikan bahwa ini hanya berfungsi jika Anda melakukan konversi ke-integer.
sumber
boost:numeric::RoundEven< double >::nearbyint
secara langsung jika Anda tidak ingin integer. @DanielWolf perhatikan bahwa fungsi sederhana diimplementasikan menggunakan +0.5 yang memiliki masalah sebagaimana ditata oleh aka.niceAnda dapat membulatkan ke n digit presisi dengan:
sumber
int
. (Dalam prakteknya pada x86, nilai FP out-of-range akan menjadikanCVTTSD2SI
hasil0x80000000
sebagai pola bit integer, yaituINT_MIN
, yang kemudian akan dikonversi kembali kedouble
.Hari-hari ini seharusnya tidak menjadi masalah untuk menggunakan kompiler C ++ 11 yang mencakup perpustakaan matematika C99 / C ++ 11. Tetapi kemudian pertanyaannya menjadi: fungsi pembulatan mana yang Anda pilih?
C99 / C ++ 11
round()
seringkali bukan fungsi pembulatan yang Anda inginkan . Ini menggunakan mode pembulatan funky yang bulat dari 0 sebagai tie-break pada kasus setengah jalan (+-xxx.5000
). Jika Anda secara khusus menginginkan mode pembulatan itu, atau Anda menargetkan implementasi C ++round()
yang lebih cepat daripadarint()
, kemudian menggunakannya (atau meniru perilakunya dengan salah satu jawaban lain pada pertanyaan ini yang membawanya pada nilai nominal dan dengan hati-hati mereproduksi yang spesifik perilaku pembulatan.)round()
Pembulatan berbeda dari putaran default IEEE754 ke mode terdekat dengan bahkan sebagai tie-break . Evenest-even menghindari bias statistik dalam besarnya rata-rata angka, tetapi bias terhadap angka genap.Ada dua fungsi pembulatan perpustakaan matematika yang menggunakan mode pembulatan default saat ini:
std::nearbyint()
danstd::rint()
, keduanya ditambahkan dalam C99 / C ++ 11, sehingga keduanya tersedia kapan sajastd::round()
. Satu-satunya perbedaan adalah yangnearbyint
tidak pernah memunculkan FE_INEXACT.Lebih suka
rint()
karena alasan kinerja : gcc dan dentang keduanya sebaris secara lebih mudah, tetapi gcc tidak pernah sebarisnearbyint()
(bahkan dengan-ffast-math
)gcc / dentang untuk x86-64 dan AArch64
Saya meletakkan beberapa fungsi tes di Matt Godbolt's Compiler Explorer , di mana Anda dapat melihat output sumber + asm (untuk beberapa kompiler). Untuk lebih lanjut tentang membaca output kompiler, lihat T&J ini , dan Matt's CppCon2017 berbicara: “Apa yang Telah Dilakukan Kompiler Saya Akhir-akhir Ini? Membuka Kunci Tutup Kompiler ” ,
Dalam kode FP, biasanya menang besar untuk sebaris fungsi kecil. Terutama pada non-Windows, di mana konvensi panggilan standar tidak memiliki register yang diawetkan oleh panggilan, sehingga kompiler tidak dapat menyimpan nilai FP apa pun di register XMM di seluruh a
call
. Jadi, bahkan jika Anda tidak benar-benar tahu asm, Anda masih dapat dengan mudah melihat apakah itu hanya buntut panggilan ke fungsi perpustakaan atau apakah itu mengarah ke satu atau dua instruksi matematika. Apa pun yang sesuai dengan satu atau dua instruksi lebih baik daripada panggilan fungsi (untuk tugas khusus ini pada x86 atau ARM).Pada x86, apa pun yang inline ke SSE4.1
roundsd
dapat di-vektorisasi secara otomatis dengan SSE4.1roundpd
(atau AVXvroundpd
). (Konversi integer FP-> juga tersedia dalam bentuk SIMD yang dikemas, kecuali untuk FP-> integer 64-bit yang memerlukan AVX512.)std::nearbyint()
:-msse4.1
.-msse4.1 -ffast-math
, dan hanya pada gcc 5.4 dan sebelumnya . Nantinya gcc tidak pernah menguraikannya (mungkin mereka tidak menyadari bahwa salah satu bit langsung dapat menekan pengecualian yang tidak tepat? Itulah yang menggunakan dentang, tetapi gcc yang lebih lama menggunakan yang sama langsung denganrint
ketika ia melakukan inline)std::rint
:-msse4.1
-msse4.1
. (Tanpa SSE4.1, sebaris ke beberapa instruksi)-ffast-math -msse4.1
.std::round
:-ffast-math -msse4.1
, membutuhkan dua konstanta vektor.std::floor
/std::ceil
/std::trunc
-msse4.1
-msse4.1
-ffast-math -msse4.1
Pembulatan ke
int
/long
/long long
:Anda memiliki dua opsi di sini: gunakan
lrint
(sepertirint
tetapi kembalilong
, ataulong long
untukllrint
), atau gunakan fungsi pembulatan FP-> FP dan kemudian konversikan ke tipe integer dengan cara normal (dengan pemotongan). Beberapa kompiler mengoptimalkan satu cara lebih baik daripada yang lain.Perhatikan bahwa
int i = lrint(x)
mengonversifloat
ataudouble
->long
pertama, dan kemudian memotong bilangan bulat keint
. Ini membuat perbedaan untuk bilangan bulat di luar kisaran: Perilaku tidak terdefinisi dalam C ++, tetapi terdefinisi dengan baik untuk instruksi x86 FP -> int (yang akan dikeluarkan oleh kompiler kecuali jika melihat UB pada waktu kompilasi saat melakukan propagasi konstan, maka itu adalah diizinkan untuk membuat kode yang rusak jika pernah dijalankan).Pada x86, konversi integer FP-> yang meluap integer menghasilkan
INT_MIN
atauLLONG_MIN
(pola0x8000000
-bit atau setara 64-bit, hanya dengan set tanda-bit). Intel menyebut ini nilai "integer tidak terbatas". (Lihat yangcvttsd2si
entri manual , instruksi SSE2 yang mengubah (dengan pemotongan) skalar ganda untuk integer ditandatangani. Ini tersedia dengan 32-bit atau tujuan 64-bit integer (dalam mode 64-bit saja). Ada jugacvtsd2si
(mualaf dengan pembulatan saat ini mode), yang ingin kita keluarkan oleh kompiler, tapi sayangnya gcc dan dentang tidak akan melakukannya tanpa-ffast-math
.unsigned
Berhati- hatilah juga bahwa FP ke / dari int / long kurang efisien pada x86 (tanpa AVX512). Konversi ke 32-bit yang tidak ditandatangani pada mesin 64-bit cukup murah; hanya mengkonversi ke 64-bit yang ditandatangani dan terpotong. Tetapi sebaliknya itu secara signifikan lebih lambat.x86 dentang dengan / tanpa
-ffast-math -msse4.1
:(int/long)rint
inline keroundsd
/cvttsd2si
. (optimasi terlewatkan kecvtsd2si
).lrint
sama sekali tidak sejajar.x86 gcc6.x dan sebelumnya tanpa
-ffast-math
: tidak ada cara inline-ffast-math
:(int/long)rint
putaran dan konversi secara terpisah (dengan 2 instruksi total SSE4.1 diaktifkan, jika tidak dengan sekelompok kode yang diuraikan untukrint
tanparoundsd
).lrint
tidak sebaris.x86 gcc dengan
-ffast-math
: semua cara sebariscvtsd2si
(optimal) , tidak perlu untuk SSE4.1.AArch64 gcc6.3 tanpa
-ffast-math
:(int/long)rint
inline ke 2 instruksi.lrint
tidak sebaris-ffast-math
:(int/long)rint
mengkompilasi panggilan kelrint
.lrint
tidak sebaris. Ini mungkin merupakan optimasi yang terlewatkan kecuali dua instruksi yang kami dapatkan tanpa-ffast-math
sangat lambat.sumber
rint()
mana itu adalah pilihan yang layak, yang biasanya terjadi. Saya kira namanyaround()
menyiratkan kepada beberapa programmer bahwa ini adalah apa yang mereka inginkan, sementararint()
tampak misterius. Catat ituround()
tidak menggunakan mode pembulatan "funky": round-to-terdekat-ikatan-jauh adalah mode pembulatan IEEE-754 (2008) resmi. Ini aneh yangnearbyint()
tidak disorot, mengingat sebagian besar samarint()
, dan harus identik dalam-ffast-math
kondisi. Itu terlihat bug-ish bagi saya.Waspadalah terhadap
floor(x+0.5)
. Inilah yang dapat terjadi untuk bilangan ganjil dalam kisaran [2 ^ 52,2 ^ 53]:Ini adalah http://bugs.squeak.org/view.php?id=7134 . Gunakan solusi seperti yang ada di @konik.
Versi saya yang kuat akan seperti:
Alasan lain untuk menghindari lantai (x + 0,5) diberikan di sini .
sumber
Jika Anda akhirnya ingin mengonversi
double
outputround()
fungsi Anda menjadiint
, maka solusi yang diterima dari pertanyaan ini akan terlihat seperti:Ini jam di sekitar 8,88 ns di mesin saya ketika melewati nilai acak seragam.
Di bawah ini secara fungsional setara, sejauh yang saya tahu, tetapi jam di 2,48 ns pada mesin saya, untuk keuntungan kinerja yang signifikan:
Di antara alasan untuk kinerja yang lebih baik adalah percabangan yang dilewati.
sumber
int
. (Dalam praktiknya pada x86, nilai -CVTTSD2SI
0x80000000
nilai FP out-of-range akan menjadikan hasil sebagai pola bit integer, yaituINT_MIN
, yang kemudian akan dikonversi kembali kedouble
.Tidak perlu menerapkan apa pun, jadi saya tidak yakin mengapa begitu banyak jawaban melibatkan definisi, fungsi, atau metode.
Di C99
Kami memiliki yang berikut dan dan tajuk <tgmath.h> untuk makro tipe-generik.
Jika Anda tidak dapat mengkompilasi ini, Anda mungkin telah meninggalkan perpustakaan matematika. Perintah yang mirip dengan ini berfungsi pada setiap kompiler C yang saya miliki (beberapa).
Dalam C ++ 11
Kami memiliki kelebihan dan kekurangan berikut di #include <cmath> yang bergantung pada floating point presisi ganda IEEE.
Ada persamaan di namespace std juga.
Jika Anda tidak dapat mengkompilasi ini, Anda mungkin menggunakan kompilasi C bukannya C ++. Perintah dasar berikut tidak menghasilkan kesalahan atau peringatan dengan g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0, dan Komunitas Visual C ++ 2015.
Dengan Divisi Ordinal
Ketika membagi dua bilangan ordinal, di mana T pendek, int, panjang, atau ordinal lainnya, ekspresi pembulatannya adalah ini.
Ketepatan
Tidak ada keraguan bahwa ketidakakuratan yang tampak aneh muncul dalam operasi floating point, tetapi ini hanya ketika angka muncul, dan tidak ada hubungannya dengan pembulatan.
Sumbernya bukan hanya jumlah digit signifikan dalam mantissa representasi IEEE dari angka floating point, ini terkait dengan pemikiran desimal kita sebagai manusia.
Sepuluh adalah produk lima dan dua, dan 5 dan 2 relatif prima. Oleh karena itu standar floating point IEEE tidak mungkin direpresentasikan dengan sempurna sebagai angka desimal untuk semua representasi digital biner.
Ini bukan masalah dengan algoritma pembulatan. Ini adalah realitas matematika yang harus dipertimbangkan selama pemilihan jenis dan desain perhitungan, entri data, dan tampilan angka. Jika suatu aplikasi menampilkan angka-angka yang menunjukkan masalah konversi biner-desimal ini, maka aplikasi secara visual menyatakan akurasi yang tidak ada dalam realitas digital dan harus diubah.
sumber
Fungsi
double round(double)
dengan penggunaanmodf
fungsi:Untuk mengkompilasi bersih, termasuk "math.h" dan "batas" diperlukan. Fungsi ini bekerja sesuai dengan skema pembulatan berikut:
sumber
rint()
ataunearbyint()
, tetapi jika Anda benar-benar tidak dapat menggunakan kompiler yang menyediakan fungsi pembulatan yang tepat, dan Anda membutuhkan ketelitian lebih dari kinerja ...Jika Anda harus dapat mengkompilasi kode di lingkungan yang mendukung standar C ++ 11, tetapi juga harus dapat mengkompilasi kode yang sama di lingkungan yang tidak mendukungnya, Anda bisa menggunakan fungsi makro untuk memilih antara std :: round () dan fungsi kustom untuk setiap sistem. Cukup lewat
-DCPP11
atau/DCPP11
ke kompiler yang memenuhi persyaratan C ++ 11 (atau gunakan makro versi bawaannya), dan buat header seperti ini:Untuk contoh cepat, lihat http://ideone.com/zal709 .
Ini mendekati std :: round () di lingkungan yang tidak memenuhi persyaratan C ++ 11, termasuk pelestarian bit tanda untuk -0.0. Namun, hal itu dapat menyebabkan sedikit peningkatan kinerja, dan kemungkinan akan mengalami masalah dengan pembulatan nilai-nilai titik-mengambang "masalah" tertentu yang diketahui seperti 0,4999999999999999994 atau nilai yang serupa.
Atau, jika Anda memiliki akses ke kompiler yang memenuhi syarat C ++ 11, Anda bisa mengambil std :: round () dari
<cmath>
headernya, dan menggunakannya untuk membuat header Anda sendiri yang mendefinisikan fungsi jika belum ditentukan. Perhatikan bahwa ini mungkin bukan solusi yang optimal, terutama jika Anda perlu mengkompilasi untuk beberapa platform.sumber
Berdasarkan respon Kalaxy, berikut ini adalah solusi templated yang membulatkan angka floating point ke tipe integer terdekat berdasarkan pembulatan alami. Itu juga melempar kesalahan dalam mode debug jika nilainya di luar kisaran tipe integer, dengan demikian melayani kira-kira sebagai fungsi perpustakaan yang layak.
sumber
0.5
tidak berfungsi dalam semua kasus. Meskipun setidaknya Anda menangani masalah luapan sehingga Anda menghindari perilaku yang tidak jelas.Seperti yang ditunjukkan dalam komentar dan jawaban lain, pustaka standar ISO C ++ tidak menambahkan
round()
hingga ISO C ++ 11, ketika fungsi ini ditarik dengan merujuk ke perpustakaan matematika standar ISO C99.Untuk operan positif di [½, ub ]
round(x) == floor (x + 0.5)
, di mana ub adalah 23 untukfloat
ketika dipetakan ke IEEE-754 (2008)binary32
, dan 2 52 untukdouble
ketika dipetakan ke IEEE-754 (2008)binary64
. Angka 23 dan 52 sesuai dengan jumlah bit mantissa yang disimpan dalam dua format floating-point ini. Untuk operan positif di [+0, ½)round(x) == 0
, dan untuk operan positif di ( ub , + ∞]round(x) == x
. Karena fungsinya simetris tentang sumbu x, argumen negatifx
dapat ditangani sesuai denganround(-x) == -round(x)
.Ini mengarah ke kode ringkas di bawah ini. Ini mengkompilasi ke dalam sejumlah instruksi mesin yang wajar di berbagai platform. Saya mengamati kode paling ringkas pada GPU, di mana
my_roundf()
membutuhkan sekitar selusin instruksi. Bergantung pada arsitektur prosesor dan toolchain, pendekatan berbasis floating-point ini bisa lebih cepat atau lebih lambat daripada implementasi berbasis integer dari newlib yang dirujuk dalam jawaban yang berbeda .Saya menguji secara
my_roundf()
mendalam terhadaproundf()
implementasi newlib menggunakan Intel compiler versi 13, dengan keduanya/fp:strict
dan/fp:fast
. Saya juga memeriksa bahwa versi newlib cocok denganroundf()
dimathimf
perpustakaan kompiler Intel. Pengujian menyeluruh tidak mungkin dilakukan untuk presisi gandaround()
, namun kode ini secara struktural identik dengan implementasi presisi tunggal.sumber
int
lebar lebih dari 16 bit. Tentu saja masih menganggap bahwa itufloat
adalah 4-byte IEEE754 binary32. C ++ 11static_assert
atau mungkin makro#ifdef
/#error
bisa memeriksanya. (Tapi tentu saja jika C ++ 11 tersedia, Anda harus menggunakanstd::round
, atau untuk penggunaan mode pembulatan saat inistd::rint
yang sejalan dengan gcc dan dentang).gcc -ffast-math -msse4.1
sebarisstd::round()
keadd( AND(x, L1), OR(x,L2)
, dan kemudian aroundsd
. yaitu itu cukup efisien alatround
dalam halrint
. Tetapi tidak ada alasan untuk melakukan ini secara manual dalam sumber C ++, karena jika Anda punyastd::rint()
ataustd::nearbyint()
Anda juga punyastd::round()
. Lihat jawaban saya untuk tautan godbolt dan ikhtisar tentang apa yang sebaris atau tidak dengan versi gcc / dentang yang berbeda.round()
efisien dalam halrint()
(ketika yang terakhir beroperasi dalam mode round-to-terdekat-atau-bahkan): Saya menerapkan itu untuk perpustakaan matematika standar CUDA. Namun, pertanyaan ini sepertinya menanyakan bagaimana mengimplementasikanround()
dengan C ++ sebelum C ++ 11, jadirint()
tidak akan tersedia, hanyafloor()
danceil()
.round()
mudah disintesis darirint()
dalam mode round-to-zero , aliastrunc()
. Seharusnya tidak merespons sebelum kopi pertama.round()
; kebanyakan programmer tidak menyadari perbedaan antararound()
vsrint()
dengan round-to-terdekat-even, di mana yang terakhir biasanya disediakan langsung oleh perangkat keras dan karenanya lebih efisien; Saya mengeja itu dalam Panduan Pemrograman CUDA untuk membuat programmer sadar: "Cara yang disarankan untuk membulatkan operan floating-point presisi tunggal ke integer, dengan hasil menjadi angka floating-point presisi tunggal adalahrintf()
, tidakroundf()
".Saya menggunakan implementasi asm untuk arsitektur x86 dan MS VS spesifik C ++ berikut:
UPD: untuk mengembalikan nilai ganda
Keluaran:
sumber
rint()
ataunearbyint()
keroundsd
instruksi SSE4.1 atau instruksi x87frndint
, yang akan jauh lebih cepat daripada dua round trip store / reload yang diperlukan untuk menggunakan asline ini pada data dalam register. MSVC inline asm sucks cukup banyak untuk membungkus instruksi tunggal sepertifrndint
karena tidak ada cara untuk mendapatkan input dalam register. Menggunakannya di akhir fungsi dengan hasilst(0)
mungkin dapat diandalkan sebagai cara untuk mengembalikan output; rupanya itu aman untukeax
bilangan bulat, bahkan ketika inline fungsi yang mengandung asm.double
, dan dengan demikian harus dapat memancarkanfrndint
dirinya sendirirint()
. Jika kompiler Anda menggunakan SSE2, memantulkandouble
dari register XMM ke x87 dan kembali mungkin tidak sepadan.Cara terbaik untuk membulatkan nilai mengambang dengan "n" tempat desimal, adalah sebagai berikut dengan dalam O (1) waktu: -
Kita harus melengkapi nilainya dengan 3 tempat yaitu n = 3.Jadi,
sumber
Ini mungkin cara konversi yang kotor dan tidak efisien tetapi, ya, berhasil lol. Dan itu bagus, karena ini berlaku untuk pelampung yang sebenarnya. Tidak hanya memengaruhi output secara visual.
sumber
Saya melakukan ini:
sumber