round () untuk float di C ++

232

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?

Roddy
sumber
1
Jika Anda hanya ingin menampilkan angka sebagai angka bulat, sepertinya Anda bisa melakukannya std::cout << std::fixed << std::setprecision(0) << -0.9, misalnya.
Frank
44
Melindungi ini ... Pengguna baru dengan skema pembulatan baru yang brilian harus membaca jawaban yang ada terlebih dahulu.
Shog9
12
roundtersedia sejak C ++ 11 in <cmath>. Sayangnya, jika Anda menggunakan Microsoft Visual Studio, perangkat ini masih hilang: connect.microsoft.com/VisualStudio/feedback/details/775474/...
Alessandro Jacopson
3
Seperti yang saya perhatikan dalam jawaban saya, menggulung sendiri roundmemiliki banyak peringatan. Sebelum C ++ 11, standar mengandalkan C90 yang tidak termasuk round. C ++ 11 bergantung pada C99 yang memang memiliki roundtetapi juga seperti yang saya catat termasuk truncyang 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.
Shafik Yaghmour
2
@uvts_cvs ini sepertinya tidak menjadi masalah dengan versi terbaru dari studio visual, lihat langsung .
Shafik Yaghmour

Jawaban:

144

Tidak ada putaran () di pustaka standar C ++ 98. Anda bisa menulis sendiri. Berikut ini adalah implementasi round-half-up :

double round(double d)
{
  return floor(d + 0.5);
}

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.

Andreas Magnusson
sumber
53
Ini tidak menangani angka negatif dengan benar. Jawaban oleh litb sudah benar.
Pengguna Terdaftar
39
@InnerJoin: Ya, ia menangani angka negatif secara berbeda untuk jawaban litb, tetapi itu tidak membuatnya "salah".
Roddy
39
Menambahkan 0,5 sebelum memotong gagal membulatkan ke bilangan bulat terdekat untuk beberapa input termasuk 0.4999999999999999494. Lihat blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
Pascal Cuoq
10
@ Sergi0: Tidak ada "benar" dan "salah" karena ada lebih dari satu definisi pembulatan yang menentukan apa yang terjadi pada titik tengah. Periksa fakta Anda sebelum menjatuhkan hukuman.
Jon
16
@MuhammadAnnaqeeb: Anda benar, banyak hal telah membaik sejak rilis C ++ 11. Pertanyaan ini ditanyakan dan dijawab di lain waktu ketika kehidupan sulit dan sukacita sedikit. Itu tetap di sini sebagai ode untuk para pahlawan yang hidup dan bertempur pada waktu itu dan bagi jiwa-jiwa miskin yang masih tidak dapat menggunakan alat-alat modern.
Andreas Magnusson
96

Boost menawarkan serangkaian fungsi pembulatan sederhana.

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

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

Untuk informasi lebih lanjut, lihat dokumentasi Boost .

Mengedit : Sejak C ++ 11, ada std::round, std::lrounddanstd::llround .

Daniel Wolf
sumber
2
Saya sudah menggunakan boost dalam proyek saya, +1 untuk ini, jauh lebih baik daripada menggunakan floor(value + 0.5)pendekatan naif !
Gustavo Maciel
@ GustavoMaciel Saya tahu saya agak terlambat ke permainan, tetapi meningkatkan implementasi floor(value + 0.5).
n. 'kata ganti' m.
Sebenarnya tidak: github.com/boostorg/math/blob/develop/include/boost/math/… 4 tahun kemudian, saya juga ingin mengatakan bahwa floor(value + 0.5)itu sama sekali tidak naif, tetapi lebih tergantung pada konteks dan sifatnya nilai yang ingin Anda bulatkan!
Gustavo Maciel
84

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 :

Perpustakaan yang dijelaskan dalam ayat 7 ISO / IEC 9899: 1990 dan ayat 7 ISO / IEC 9899 / Amd.1: 1995 selanjutnya disebut Perpustakaan Standar C. 1)

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 :

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

Pilihan lain juga dari C99 adalah std :: trunc yang:

Menghitung bilangan bulat terdekat tidak lebih besar dari pada arg.

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

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::floordan ditambahkan oleh implementasi Anda 0.5tidak berfungsi untuk semua input:

double myround(double d)
{
  return std::floor(d + 0.5);
}

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 ):

Nilai awal dari tipe floating point dapat dikonversi ke nilai awal dari tipe integer. Konversi terpotong; yaitu, bagian pecahan dibuang. Perilaku tidak terdefinisi jika nilai terpotong tidak dapat diwakili dalam tipe tujuan. [...]

Sebagai contoh:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

Mengingat std::numeric_limits<unsigned int>::max()adalah 4294967295maka panggilan berikut:

myround( 4294967296.5f ) 

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:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

Di sisi lain, jika tidak ada solusi lain yang dapat digunakan, newlib berpotensi menjadi pilihan karena merupakan implementasi yang teruji dengan baik.

Shafik Yaghmour
sumber
5
@ downvoter tolong jelaskan apa yang bisa diperbaiki? Sebagian besar jawaban di sini salah karena mereka mencoba untuk menggulung putaran mereka sendiri yang semuanya gagal dalam satu atau lain bentuk. Jika ada sesuatu yang hilang dalam penjelasan saya, beri tahu saya.
Shafik Yaghmour
1
Jawaban lengkap yang bagus - terutama hanya di bawah 0,5 bagian. Ceruk lain: round(-0.0). C spec tampaknya tidak menentukan. Saya harapkan -0.0sebagai hasilnya.
chux - Reinstate Monica
3
@ chux menarik, dan standar IEEE 754-2008 tidak menentukan bahwa pembulatan mempertahankan tanda-tanda nol dan tak terhingga (lihat 5.9).
Ruslan
1
@ Shafik, ini jawaban yang bagus. Saya tidak pernah berpikir bahwa pembulatan adalah operasi non-sepele.
Ruslan
1
Mungkin layak disebut yang std::rint()sering lebih disukai std::round()ketika C ++ 11 tersedia karena alasan numerik dan kinerja. Ini menggunakan mode pembulatan saat ini, tidak seperti round()mode khusus. Ini bisa jauh lebih efisien pada x86, di mana rintbisa sejajar dengan satu instruksi. (gcc dan dentang melakukan itu bahkan tanpa -ffast-math godbolt.org/g/5UsL2e , sementara hanya dentang inline yang hampir setara nearbyint()) ARM memiliki dukungan instruksi tunggal untuk round(), tetapi pada x86 hanya dapat sejalan dengan beberapa instruksi, dan hanya dengan-ffast-math
Peter Cordes
71

Mungkin perlu dicatat bahwa jika Anda menginginkan hasil bilangan bulat dari pembulatan, Anda tidak perlu melewati langit-langit atau lantai. Yaitu,

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}
kalaxy
sumber
3
Meskipun tidak memberikan hasil yang diharapkan untuk 0,4999999999999999994 (well, tergantung pada apa yang Anda harapkan tentu saja, tetapi 0 tampaknya lebih masuk akal bagi saya daripada 1)
stijn
@stijn Tangkapan bagus. Saya menemukan bahwa menambahkan akhiran literal ganda yang panjang ke konstanta saya memperbaiki masalah contoh Anda, tetapi saya tidak tahu apakah ada contoh presisi lain yang tidak akan ditangkap.
kalaxy
1
btw jika Anda menambahkan 0,49999999999999994 bukan 0,5, itu berfungsi ok untuk keduanya 0,4999999999999999994 dan 5000000000000001.0.0 sebagai input. Tidak yakin apakah itu ok untuk semua nilai, dan saya tidak dapat menemukan referensi yang menyatakan bahwa ini adalah perbaikan terbaik.
stijn
1
@stijn Tidak masalah untuk semua nilai, jika Anda tidak peduli ke arah mana nilai tepat di antara dua bilangan bulat dibulatkan. Tanpa berpikir, saya akan membuktikannya dengan analisis kasus dengan kasus-kasus berikut: 0 <= d <0,5, 0,5 <= d <1,5, 1,5 <= d <2 ^ 52, d> = 2 ^ 52. Saya juga secara mendalam menguji casing presisi tunggal.
Pascal Cuoq
3
Per 4.9 [conv.fpint], "Perilaku tidak terdefinisi jika nilai terpotong tidak dapat diwakili dalam tipe tujuan." , jadi ini sedikit berbahaya. Jawaban SO lainnya menjelaskan cara melakukannya dengan kuat.
Tony Delroy
41

Ini tersedia sejak C ++ 11 dalam cmath (menurut http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

Keluaran:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2
schibum
sumber
1
ada juga lrounddan llrounduntuk hasil yang tidak terpisahkan
sp2danny
@ sp2danny: atau lebih baik, lrintuntuk menggunakan mode pembulatan saat ini daripada roundtiebreak funky away-from-zero.
Peter Cordes
27

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.

MSN
sumber
1
Adalah baik untuk membuat perbedaan antara berbagai versi 'bulat'. Adalah baik untuk mengetahui kapan harus memilih yang mana juga.
xtofl
5
Memang ada algoritma pembulatan yang berbeda yang semuanya dapat membuat klaim yang masuk akal untuk menjadi "benar". Namun lantai (nilai + 0,5) bukan salah satunya. Untuk beberapa nilai, seperti 0,49999997f atau setara ganda, jawabannya salah - itu akan dibulatkan menjadi 1,0 ketika semua setuju bahwa itu harus nol. Lihat posting ini untuk perincian: blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
Bruce Dawson
14

Ada 2 masalah yang kita lihat:

  1. konversi pembulatan
  2. konversi tipe.

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

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

dari: http://www.cs.tut.fi/~jkorpela/round.html

Sangeet
sumber
Menggunakan LONG_MIN-0.5dan LONG_MAX+0.5 memperkenalkan komplikasi karena matematika mungkin tidak tepat. LONG_MAXdapat melebihi doublepresisi untuk konversi yang tepat. Lebih lanjut kemungkinan ingin assert(x < LONG_MAX+0.5); (<vs <=) karena LONG_MAX+0.5mungkin benar-benar dapat diwakili dan (x)+0.5mungkin memiliki hasil yang pasti LONG_MAX+1yang gagal longdilemparkan. Masalah sudut lainnya juga.
chux
Jangan panggil fungsi Anda round(double), sudah ada fungsi perpustakaan matematika standar dari nama itu (dalam C ++ 11) sehingga membingungkan. Gunakan std::lrint(x)jika tersedia.
Peter Cordes
11

Jenis pembulatan tertentu juga diterapkan dalam Peningkatan:

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

Perhatikan bahwa ini hanya berfungsi jika Anda melakukan konversi ke-integer.

Philipp
sumber
2
Boost juga menawarkan serangkaian fungsi pembulatan sederhana; lihat jawaban saya.
Daniel Wolf
Anda juga dapat menggunakan boost:numeric::RoundEven< double >::nearbyintsecara langsung jika Anda tidak ingin integer. @DanielWolf perhatikan bahwa fungsi sederhana diimplementasikan menggunakan +0.5 yang memiliki masalah sebagaimana ditata oleh aka.nice
stijn
6

Anda dapat membulatkan ke n digit presisi dengan:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}
Carl
sumber
4
Kecuali ukuran int compiler Anda default ke 1024 bit, ini tidak akan akurat untuk double besar ...
alias. Bagus
Saya pikir itu dapat diterima mengingat kapan akan digunakan: Jika nilai ganda Anda adalah 1,0 e + 19, membulatkan ke 3 tempat tidak masuk akal.
Carl
3
tentu, tetapi pertanyaannya adalah untuk putaran generik, dan Anda tidak dapat mengontrol bagaimana itu akan digunakan. Tidak ada alasan untuk putaran gagal di mana langit-langit dan lantai tidak akan.
aka.nice
Ini memiliki perilaku yang tidak terdefinisi untuk arg di luar kisaran int. (Dalam prakteknya pada x86, nilai FP out-of-range akan menjadikan CVTTSD2SIhasil0x80000000 sebagai pola bit integer, yaitu INT_MIN, yang kemudian akan dikonversi kembali ke double.
Peter Cordes
5

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 daripada rint(), 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()dan std::rint(), keduanya ditambahkan dalam C99 / C ++ 11, sehingga keduanya tersedia kapan saja std::round(). Satu-satunya perbedaan adalah yang nearbyinttidak pernah memunculkan FE_INEXACT.

Lebih suka rint()karena alasan kinerja : gcc dan dentang keduanya sebaris secara lebih mudah, tetapi gcc tidak pernah sebaris nearbyint()(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 roundsddapat di-vektorisasi secara otomatis dengan SSE4.1 roundpd(atau AVX vroundpd). (Konversi integer FP-> juga tersedia dalam bentuk SIMD yang dikemas, kecuali untuk FP-> integer 64-bit yang memerlukan AVX512.)

  • std::nearbyint():

    • x86 clang: inlines ke satu insn dengan -msse4.1.
    • x86 gcc: inline hanya dengan satu insn dengan -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 dengan rintketika ia melakukan inline)
    • AArch64 gcc6.3: inline ke satu insn secara default.
  • std::rint:

    • x86 clang: inlines ke satu insn dengan -msse4.1
    • x86 gcc7: sebaris dengan satu insn dengan -msse4.1. (Tanpa SSE4.1, sebaris ke beberapa instruksi)
    • x86 gcc6.x dan sebelumnya: inline ke satu insn dengan -ffast-math -msse4.1.
    • AArch64 gcc: inline ke satu insn secara default
  • std::round:

    • x86 dentang: tidak sebaris
    • x86 gcc: sebaris dengan banyak instruksi dengan -ffast-math -msse4.1, membutuhkan dua konstanta vektor.
    • AArch64 gcc: sebaris ke satu instruksi (dukungan HW untuk mode pembulatan ini serta default IEEE dan kebanyakan lainnya.)
  • std::floor/ std::ceil/std::trunc

    • x86 clang: inlines ke satu insn dengan -msse4.1
    • x86 gcc7.x: inline ke satu insn dengan -msse4.1
    • x86 gcc6.x dan sebelumnya: inline ke satu insn dengan -ffast-math -msse4.1
    • AArch64 gcc: sebaris secara default ke satu instruksi

Pembulatan ke int/ long/ long long:

Anda memiliki dua opsi di sini: gunakan lrint(seperti rinttetapi kembali long, atau long longuntuk llrint), 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.

long l = lrint(x);

int  i = (int)rint(x);

Perhatikan bahwa int i = lrint(x)mengonversi floatatau double-> longpertama, dan kemudian memotong bilangan bulat ke int. 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_MINatau LLONG_MIN(pola 0x8000000-bit atau setara 64-bit, hanya dengan set tanda-bit). Intel menyebut ini nilai "integer tidak terbatas". (Lihat yang cvttsd2sientri 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 juga cvtsd2si(mualaf dengan pembulatan saat ini mode), yang ingin kita keluarkan oleh kompiler, tapi sayangnya gcc dan dentang tidak akan melakukannya tanpa -ffast-math.

unsignedBerhati- 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)rintinline ke roundsd/ cvttsd2si. (optimasi terlewatkan ke cvtsd2si). lrintsama sekali tidak sejajar.

  • x86 gcc6.x dan sebelumnya tanpa -ffast-math: tidak ada cara inline

  • x86 gcc7 tanpa -ffast-math: (int/long)rintputaran dan konversi secara terpisah (dengan 2 instruksi total SSE4.1 diaktifkan, jika tidak dengan sekelompok kode yang diuraikan untuk rinttanpa roundsd). lrinttidak sebaris.
  • x86 gcc dengan -ffast-math : semua cara sebaris cvtsd2si(optimal) , tidak perlu untuk SSE4.1.

  • AArch64 gcc6.3 tanpa -ffast-math: (int/long)rintinline ke 2 instruksi. lrinttidak sebaris

  • AArch64 gcc6.3 dengan -ffast-math: (int/long)rintmengkompilasi panggilan ke lrint. lrinttidak sebaris. Ini mungkin merupakan optimasi yang terlewatkan kecuali dua instruksi yang kami dapatkan tanpa -ffast-mathsangat lambat.
Peter Cordes
sumber
TODO: ICC dan MSVC juga tersedia di Godbolt, tapi saya belum melihat hasilnya untuk ini. suntingan selamat datang ... Juga: apakah akan lebih bermanfaat untuk dipecah oleh kompiler / versi terlebih dahulu dan kemudian oleh fungsi di dalamnya? Kebanyakan orang tidak akan mengganti kompiler berdasarkan seberapa baik mereka mengkompilasi pembulatan bilangan bulat FP-> FP atau FP->.
Peter Cordes
2
+1 untuk merekomendasikan di rint()mana itu adalah pilihan yang layak, yang biasanya terjadi. Saya kira namanya round()menyiratkan kepada beberapa programmer bahwa ini adalah apa yang mereka inginkan, sementara rint()tampak misterius. Catat ituround() tidak menggunakan mode pembulatan "funky": round-to-terdekat-ikatan-jauh adalah mode pembulatan IEEE-754 (2008) resmi. Ini aneh yang nearbyint()tidak disorot, mengingat sebagian besar sama rint(), dan harus identik dalam -ffast-mathkondisi. Itu terlihat bug-ish bagi saya.
njuffa
4

Waspadalah terhadap floor(x+0.5). Inilah yang dapat terjadi untuk bilangan ganjil dalam kisaran [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

Ini adalah http://bugs.squeak.org/view.php?id=7134 . Gunakan solusi seperti yang ada di @konik.

Versi saya yang kuat akan seperti:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

Alasan lain untuk menghindari lantai (x + 0,5) diberikan di sini .

alias. bagus
sumber
2
Saya tertarik untuk mengetahui tentang downvotes. Apakah itu karena ikatan diselesaikan dari nol daripada ke yang terdekat?
aka.nice
1
Catatan: spec C mengatakan "pembulatan setengah kasus jauh dari nol, terlepas dari arah pembulatan saat ini.", Jadi pembulatan tanpa memperhatikan ganjil / genap sesuai.
chux - Reinstate Monica
4

Jika Anda akhirnya ingin mengonversi doubleoutput round()fungsi Anda menjadi int, maka solusi yang diterima dari pertanyaan ini akan terlihat seperti:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

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:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

Di antara alasan untuk kinerja yang lebih baik adalah percabangan yang dilewati.

dshin
sumber
Ini memiliki perilaku yang tidak terdefinisi untuk arg di luar kisaran int. (Dalam praktiknya pada x86, nilai -CVTTSD2SI0x80000000 nilai FP out-of-range akan menjadikan hasil sebagai pola bit integer, yaitu INT_MIN, yang kemudian akan dikonversi kembali ke double.
Peter Cordes
2

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.

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

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).

gcc -lm -std=c99 ...

Dalam C ++ 11

Kami memiliki kelebihan dan kekurangan berikut di #include <cmath> yang bergantung pada floating point presisi ganda IEEE.

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

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.

g++ -std=c++11 -Wall

Dengan Divisi Ordinal

Ketika membagi dua bilangan ordinal, di mana T pendek, int, panjang, atau ordinal lainnya, ekspresi pembulatannya adalah ini.

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

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.

FauChristian
sumber
1
"Saya tidak yakin mengapa begitu banyak jawaban melibatkan definisi, fungsi, atau metode." Lihatlah ketika ditanya - C ++ 11 belum keluar. ;)
jaggedSpire
@jaggedSpire, beri saya jempol, jika Anda merasa itu tepat, karena semua jawaban dengan skor tinggi sudah usang dan menyesatkan dalam konteks kompiler yang paling sering digunakan saat ini.
FauChristian
2

Fungsi double round(double)dengan penggunaan modffungsi:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

Untuk mengkompilasi bersih, termasuk "math.h" dan "batas" diperlukan. Fungsi ini bekerja sesuai dengan skema pembulatan berikut:

  • babak 5.0 adalah 5.0
  • babak 3.8 adalah 4.0
  • babak 2.3 adalah 2.0
  • babak 1.5 adalah 2.0
  • ronde 0,501 adalah 1,0
  • putaran 0,5 adalah 1,0
  • putaran 0,499 adalah 0,0
  • putaran 0,01 adalah 0,0
  • putaran 0,0 adalah 0,0
  • ronde -0,01 adalah -0,0
  • ronde -0,499 adalah -0,0
  • ronde -0,5 adalah -0,0
  • ronde -0.501 adalah -1.0
  • ronde -1.5 adalah -1.0
  • ronde -2.3 adalah -2.0
  • ronde -3.8 adalah -4.0
  • ronde -5.0 adalah -5.0
konik
sumber
2
Ini solusi yang bagus. Saya tidak yakin bahwa pembulatan -1.5 ke -1.0 adalah standar, saya harapkan -2.0 berdasarkan simetri. Saya juga tidak melihat inti dari penjaga utama, dua penjaga pertama jika bisa dilepas.
aka.nice
2
Saya memeriksa standar ISO / IEC 10967-2, open-std.org/jtc1/sc22/wg11/docs/n462.pdf dan dari lampiran B.5.2.4, fungsi pembulatan memang harus simetris, rounding_F (x) = neg_F (rounding_F (neg_F (x)))
alias.nice
Ini akan menjadi lambat dibandingkan dengan C ++ 11 rint()atau nearbyint(), tetapi jika Anda benar-benar tidak dapat menggunakan kompiler yang menyediakan fungsi pembulatan yang tepat, dan Anda membutuhkan ketelitian lebih dari kinerja ...
Peter Cordes
1

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 -DCPP11atau /DCPP11ke kompiler yang memenuhi persyaratan C ++ 11 (atau gunakan makro versi bawaannya), dan buat header seperti ini:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

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.

Justin Time - Pasang kembali Monica
sumber
1

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.

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }
bergalah
sumber
1
Seperti yang saya tunjukkan dalam jawaban saya, menambahkan 0.5tidak berfungsi dalam semua kasus. Meskipun setidaknya Anda menangani masalah luapan sehingga Anda menghindari perilaku yang tidak jelas.
Shafik Yaghmour
1

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 untuk floatketika dipetakan ke IEEE-754 (2008) binary32, dan 2 52 untuk doubleketika 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 negatif xdapat 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 terhadap roundf()implementasi newlib menggunakan Intel compiler versi 13, dengan keduanya /fp:strictdan /fp:fast. Saya juga memeriksa bahwa versi newlib cocok dengan roundf()di mathimfperpustakaan kompiler Intel. Pengujian menyeluruh tidak mungkin dilakukan untuk presisi ganda round(), namun kode ini secara struktural identik dengan implementasi presisi tunggal.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}
njuffa
sumber
Saya melakukan pengeditan untuk menghindari anggapan intlebar lebih dari 16 bit. Tentu saja masih menganggap bahwa itu floatadalah 4-byte IEEE754 binary32. C ++ 11 static_assertatau mungkin makro #ifdef/ #errorbisa memeriksanya. (Tapi tentu saja jika C ++ 11 tersedia, Anda harus menggunakan std::round, atau untuk penggunaan mode pembulatan saat ini std::rintyang sejalan dengan gcc dan dentang).
Peter Cordes
BTW, gcc -ffast-math -msse4.1sebaris std::round()ke add( AND(x, L1), OR(x,L2), dan kemudian a roundsd. yaitu itu cukup efisien alat rounddalam hal rint. Tetapi tidak ada alasan untuk melakukan ini secara manual dalam sumber C ++, karena jika Anda punya std::rint()atau std::nearbyint()Anda juga punya std::round(). Lihat jawaban saya untuk tautan godbolt dan ikhtisar tentang apa yang sebaris atau tidak dengan versi gcc / dentang yang berbeda.
Peter Cordes
@PeterCordes Saya sangat sadar bagaimana menerapkan secara round()efisien dalam hal rint()(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 mengimplementasikan round()dengan C ++ sebelum C ++ 11, jadi rint()tidak akan tersedia, hanya floor()dan ceil().
njuffa
@PeterCordes Maaf, saya salah bicara. round()mudah disintesis dari rint()dalam mode round-to-zero , alias trunc(). Seharusnya tidak merespons sebelum kopi pertama.
njuffa
1
@PeterCordes Saya setuju bahwa kemungkinan OP tidak memerlukan perilaku pembulatan khusus round(); kebanyakan programmer tidak menyadari perbedaan antara round()vs rint()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 adalah rintf(), tidak roundf()".
njuffa
0

Saya menggunakan implementasi asm untuk arsitektur x86 dan MS VS spesifik C ++ berikut:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD: untuk mengembalikan nilai ganda

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

Keluaran:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000
Aleksey F.
sumber
Nilai hasil harus nilai floating point dengan presisi ganda.
truthseeker
@ truthseeker: Ya, saya harus melihat jenis nilai pengembalian yang diperlukan. OK, lihat "UPD".
Aleksey F.
Kompiler diharapkan akan inline rint()atau nearbyint()ke roundsdinstruksi SSE4.1 atau instruksi x87 frndint, 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 seperti frndintkarena tidak ada cara untuk mendapatkan input dalam register. Menggunakannya di akhir fungsi dengan hasil st(0)mungkin dapat diandalkan sebagai cara untuk mengembalikan output; rupanya itu aman untuk eaxbilangan bulat, bahkan ketika inline fungsi yang mengandung asm.
Peter Cordes
@PeterCordes Optimasi modern dipersilakan. Namun saya tidak dapat menggunakan SSE4.1 karena tidak ada pada saat itu. Tujuan saya adalah untuk memberikan implementasi minimal putaran yang dapat berfungsi bahkan pada keluarga Intel P3 atau P4 lama dari tahun 2000-an.
Aleksey F.
P3 bahkan tidak memiliki SSE2, jadi kompiler sudah akan menggunakan x87 untuk double, dan dengan demikian harus dapat memancarkan frndintdirinya sendiri rint(). Jika kompiler Anda menggunakan SSE2, memantulkan doubledari register XMM ke x87 dan kembali mungkin tidak sepadan.
Peter Cordes
0

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,

float a=47.8732355;
printf("%.3f",a);
Parveen Kumar
sumber
-4
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

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.

Brad
sumber
Ini meriah tidak efisien, dan juga memotong (dengan selalu membuang angka trailing) daripada membulatkan ke terdekat.
Peter Cordes
-6

Saya melakukan ini:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}
Peter Mortensen
sumber
3
Bukankah maksud Anda pow (10, tempat) daripada operator biner ^ di 10 ^ tempat? 10 ^ 2 pada mesin saya memberi saya 8 !! Namun demikian pada Mac 10.7.4 dan gcc saya, kode tidak berfungsi, mengembalikan nilai aslinya.
Pete855217