Saya perlu sederhana floating point pembulatan fungsi, dengan demikian:
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
Saya bisa menemukan ceil()
dan floor()
dalam matematika.h - tapi tidak bulat()
.
Itu hadir dalam C++ standar library dengan nama lain, atau hilang??
Ada's tidak bulat() dalam C++98 perpustakaan standar. Anda dapat menulis sendiri sekalipun. Berikut ini merupakan implementasi dari bulat-setengah-up:
double round(double d)
{
return floor(d + 0.5);
}
Kemungkinan alasan tidak ada putaran fungsi dalam C++98 standar perpustakaan adalah bahwa hal itu sebenarnya bisa dilaksanakan dengan cara yang berbeda. Di atas adalah salah satu cara yang umum tapi ada yang lain seperti putaran-untuk-bahkan, yang kurang bias dan umumnya lebih baik jika anda'kembali akan melakukan banyak pembulatan; it's sedikit lebih kompleks untuk diimplementasikan meskipun.
Boost menawarkan satu set sederhana dari pembulatan fungsi.
#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 Boost documentation.
Edit: Sejak C++11, ada std::round
, std::lround
, dan std::llround
.
C++03 standar bergantung pada C90 standar untuk apa standar panggilan Standar C Library yang tercakup dalam rancangan C++03 standar (terdekat yang tersedia secara publik draf standar untuk C++03 N1804) bagian 1.2
referensi Normatif:
perpustakaan yang dijelaskan dalam klausul 7 dari sni ISO/IEC 9899:1990 dan pasal 7 dari ISO/IEC 9899/Amd.1:1995 yang selanjutnya disebut Standar C Perpustakaan.1)
Jika kita pergi ke C dokumentasi bulat, lround, llround pada cppreference kita dapat melihat bahwa bulat dan fungsi-fungsi yang terkait adalah bagian dari C99 dan dengan demikian tidak't akan tersedia pada C++03 atau sebelum.
Dalam C++11 perubahan ini sejak C++11 bergantung pada C99 rancangan standar standar C library dan karena itu memberikan std::bulat dan integral untuk kembali jenis 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 akan std::trunc yang:
Menghitung integer terdekat tidak lebih besar dalam besarnya dari 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 dukungan non C++11 aplikasi terbaik anda akan menggunakan boost bulat, iround, lround, llround atau boost trunc.
Rolling versi anda sendiri dari putaran lebih keras
Bergulir anda sendiri mungkin tidak sepadan dengan upaya yang lebih Sulit daripada yang terlihat: pembulatan mengapung ke integer terdekat, bagian 1, Pembulatan mengapung ke integer terdekat, bagian 2 dan Pembulatan mengapung ke integer terdekat, bagian 3 menjelaskan:
Misalnya yang umum roll implementasi menggunakan std::floor
, dan menambahkan 0.5
tidak bekerja untuk semua input:
double myround(double d)
{
return std::floor(d + 0.5);
}
Salah satu masukan ini akan gagal untuk 0.49999999999999994
, (melihat itu live).
Lain yang umum pelaksanaannya melibatkan casting floating point tipe tipe integral, yang dapat memanggil terdefinisi perilaku dalam kasus di mana bagian integral yang tidak dapat diwakili dalam jenis tujuan. Kita dapat melihat ini dari draft standar C++ bagian 4.9
Floating-integral konversi yang mengatakan (penekanan tambang):
prvalue dari floating point tipe dapat dikonversi ke prvalue dari tipe integer. Konversi memotong; itu adalah, bagian pecahan dibuang. Perilaku yang tidak terdefinisi jika nilai tidak terpotong dapat diwakili dalam jenis tujuan.[...]
Misalnya:
float myround(float f)
{
return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}
Diberikan std::numeric_limits<unsigned int>::max()
adalah 4294967295
maka panggilan berikut:
myround( 4294967296.5f )
akan menyebabkan overflow, (melihat itu live).
Kita dapat melihat betapa sulitnya ini benar-benar adalah dengan melihat jawaban ini untuk cara Ringkas untuk melaksanakan putaran() dalam C? yang referensi newlibs versi single precision float bulat. Itu adalah waktu yang sangat lama fungsi untuk sesuatu yang tampaknya sederhana. Tampaknya tidak mungkin bahwa siapa pun tanpa pengetahuan yang mendalam tentang floating point implementasi bisa benar melaksanakan 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 diuji dengan baik pelaksanaannya.
It's tersedia sejak C++11 di 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;
}
Output:
round(0.5): 1
round(-0.5): -1
round(1.4): 1
round(-1.4): -1
round(1.6): 2
round(-1.6): -2
It's biasanya diimplementasikan sebagai floor(nilai + 0.5)
.
Edit: dan's mungkin tidak disebut putaran karena setidaknya ada tiga pembulatan algoritma yang saya tahu: bulat dengan nol, bulat untuk bulat terdekat, dan banker's pembulatan. Anda meminta untuk putaran ke integer terdekat.
Ada 2 masalah yang kita cari di:
Pembulatan konversi berarti pembulatan ± float/double terdekat lantai/ceil float/double. Mungkin masalah anda berakhir di sini. Tapi jika anda diharapkan untuk kembali Int/Panjang, anda perlu melakukan konversi tipe, dan dengan demikian "Overflow" masalah mungkin memukul solusi anda. JADI, apakah yang memeriksa 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))
Jenis tertentu dari pembulatan juga dilaksanakan di Boost:
#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 bekerja jika anda melakukan ke-integer konversi.
Jika anda akhirnya ingin mengkonversi double
output dari anda bulat()
fungsi untuk sebuah int
, kemudian diterima solusi dari pertanyaan ini akan terlihat seperti ini:
int roundint(double r) {
return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}
Ini jam di sekitar 8.88 ns pada mesin saya ketika berlalu di acak seragam-nilai.
Di bawah ini adalah fungsional setara, sejauh yang saya tahu, tapi jam di di 2.48 ns pada mesin saya, untuk kinerja yang signifikan keuntungan:
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 dilewati bercabang.
Waspadalah terhadap floor(x+0.5)
. Berikut adalah apa yang bisa terjadi untuk angka ganjil dalam jangkauan [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. Menggunakan solusi seperti salah satu dari @konik.
Saya sendiri versi yang kuat akan menjadi sesuatu 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.
round()
sering tidak benar-benar pembulatan fungsi yang anda inginkan. Menggunakan funky mode pembulatan bahwa putaran yang dimulai dari 0 sebagai tie-break pada setengah-setengah kasus (+-xxx.5000
). Jika anda secara khusus ingin bahwa pembulatan mode, atau anda're menargetkan C++ pelaksanaan mana round()
lebih cepat dari edia()
, kemudian menggunakannya (atau meniru perilaku dengan salah satu jawaban pada pertanyaan ini yang mengambil nilai di wajah dan hati-hati direproduksi tertentu pembulatan mendatang.)
round()
's pembulatan berbeda dari IEEE754 default putaran ke terdekat mode dengan bahkan sebagai tie-break. Terdekat-bahkan menghindari statistik bias rata-rata besarnya angka-angka, tetapi tidak bias terhadap angka-angka genap.
Ada dua perpustakaan matematika fungsi pembulatan yang digunakan saat ini default mode pembulatan: std::nearbyint()
dan std::rint()
, baik ditambahkan dalam C99/C++11, sehingga mereka're tersedia setiap saat std::round()
adalah. Satu-satunya perbedaan adalah bahwa nearbyint
tidak pernah menimbulkan FE_INEXACT.
Sukai edia()
untuk alasan kinerja: gcc dan dentang kedua inline itu lebih mudah, tapi gcc tidak pernah inlines nearbyint()
(bahkan dengan -mousepad, merek-matematika
) Aku menaruh beberapa tes fungsi pada Matt Godbolt's Compiler Explorer, di mana anda dapat melihat sumber + asm output (untuk beberapa compiler). Untuk lebih lanjut tentang membaca compiler output, lihat Q&A, dan Matt's CppCon2017 bicara: ["Apa yang Telah Saya Compiler Lakukan untuk Saya akhir-Akhir ini? Unbolting Compiler's Lid"][6],
Di FP kode, it's biasanya menang besar untuk inline kecil fungsi. Terutama pada non-Windows, di mana standar konvensi pemanggilan tidak memiliki call-diawetkan register, sehingga penyusun dapat't menjaga setiap FP nilai-nilai dalam XMM register di panggilan
. Jadi bahkan jika anda don't benar-benar tahu asm, anda masih dapat dengan mudah melihat apakah itu's hanya ekor-panggilan ke fungsi perpustakaan atau apakah itu inline untuk satu atau dua matematika petunjuk. Apa pun yang inlines untuk satu atau dua petunjuk yang lebih baik dari fungsi panggilan (untuk tugas khusus ini pada x86 atau ARM).
Pada x86, apa pun yang inlines untuk SSE4.1 roundsd
bisa auto-vectorize dengan SSE4.1 roundpd
(atau AVX vroundpd
). (FP->integer konversi juga tersedia dalam dikemas SIMD bentuk, kecuali untuk FP->64-bit integer yang membutuhkan AVX512.)
std::nearbyint()
: -msse4.1
. -msse4.1 -mousepad, merek-matematika
, dan hanya pada gcc 5.4 dan sebelumnya. Kemudian gcc tidak pernah inlines itu (mungkin mereka didn't menyadari bahwa salah satu segera bit dapat menekan eksak pengecualian? Yang's apa dentang menggunakan, tetapi yang lebih tua menggunakan gcc langsung sama seperti untuk edia
ketika itu tidak inline itu) std::rint
: -msse4.1
-msse4.1
. (Tanpa SSE4.1, inlines untuk beberapa instruksi) -mousepad, merek-matematika -msse4.1
. std::round
: -mousepad, merek-matematika -msse4.1
, membutuhkan dua vektor konstanta. std::floor
/ std::ceil
/ std::trunc
-msse4.1
-msse4.1
-mousepad, merek-matematika -msse4.1
int
/ lama
/ lama
:Anda memiliki dua pilihan berikut ini: gunakan lrint
(seperti edia
tapi kembali lama
, atau lama
untuk llrint
), atau gunakan FP->FP fungsi dan kemudian dikonversi ke integer tipe normal (dengan pemotongan). Beberapa kompiler untuk mengoptimalkan salah satu cara yang lebih baik dari yang lain.
long l = lrint(x);
int i = (int)rint(x);
Perhatikan bahwa int i = lrint(x)
mengubah float
atau double
-> lama
pertama, dan kemudian memotong integer untuk int
. Hal ini membuat perbedaan untuk out-of-range bilangan bulat: Perilaku tidak Terdefinisi di C++, tapi didefinisikan untuk x86 FP -> int instruksi (yang kompilator akan memancarkan kecuali melihat UB pada waktu kompilasi saat melakukan konstanta propagasi, maka's diizinkan untuk membuat kode yang istirahat jika itu's yang pernah dijalankan).
Pada x86, FP->integer konversi yang meluap integer menghasilkan INT_MIN
atau LLONG_MIN
(sedikit-pola 0x8000000
atau 64-bit setara, dengan hanya tanda-bit set). Intel menyebut ini "integer terbatas" nilai. (Lihat cvttsd2si
entri manual, dengan instruksi SSE2 yang mengubah (dengan pemotongan) skalar ganda untuk signed integer. It's tersedia dengan 32-bit atau 64-bit integer tujuan (dalam mode 64-bit saja). Ada's juga cvtsd2si
(mengkonversi dengan saat ini mode pembulatan), yang adalah apa yang kita'd seperti kompilator untuk memancarkan, tapi sayangnya gcc dan dentang won't melakukan itu tanpa -mousepad, merek-matematika
.
Juga berhati-hatilah bahwa FP ke/dari unsigned
int / panjang kurang efisien pada x86 (tanpa AVX512). Konversi untuk 32-bit unsigned pada mesin 64-bit ini cukup murah, hanya mengkonversi ke 64-bit ditandatangani dan memotong. Tapi selain itu's ini secara signifikan lebih lambat.
-mousepad, merek-matematika -msse4.1
: (int/panjang)edia
inlines untuk roundsd
/ cvttsd2si
. (terjawab optimasi untuk cvtsd2si
). lrint
doesn't inline sama sekali. -mousepad, merek-matematika
: tidak cara inlines -mousepad, merek-matematika
: (int/panjang)edia
putaran dan mengkonversi secara terpisah (dengan 2 total instruksi SSE4.1 diaktifkan, jika tidak dengan sekelompok kode inline untuk edia
tanpa roundsd
). lrint
doesn't inline. -mousepad, merek-matematika
: semua cara inline untuk cvtsd2si
(optimal) tidak perlu untuk SSE4.1. -mousepad, merek-matematika
: (int/panjang)edia
inlines untuk 2 petunjuk. lrint
doesn't inline -mousepad, merek-matematika
: (int/panjang)edia
mengkompilasi panggilan untuk lrint
. lrint
doesn't inline. Ini mungkin akan terjawab optimasi kecuali dua petunjuk yang kita dapatkan tanpa -mousepad, merek-matematika
sangat lambat. Fungsi double round(double)
dengan menggunakan modf
fungsi:
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 "matematika.h" dan "batas" diperlukan. Fungsi kerjanya sesuai dengan yang berikut skema pembulatan:
Ada tidak perlu untuk menerapkan apa-apa, jadi saya'm tidak yakin mengapa begitu banyak jawaban melibatkan mendefinisikan, fungsi, atau metode.
Di C99
Kami memiliki berikut dan dan header <tgmath.h> untuk jenis-generic makro.
#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);
Jika anda tidak bisa mengkompilasi ini, anda mungkin telah ditinggalkan perpustakaan matematika. Perintah yang mirip dengan ini bekerja pada setiap compiler C yang saya miliki (beberapa).
gcc -lm -std=c99 ...
Dalam C++11
Kami memiliki berikut dan tambahan overload di #include <cmath> yang mengandalkan IEEE double precision floating point.
#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);
Ada setara dalam namespace std juga.
Jika anda tidak bisa mengkompilasi ini, anda dapat menggunakan C kompilasi bukan C++. Berikut perintah dasar tidak menghasilkan kesalahan atau peringatan dengan g++ 6.3.1, x86_64-w64-mingw32-g++ 6.3.0, dentang-x86_64++ 3.8.0, dan Visual C++ 2015 Masyarakat.
g++ -std=c++11 -Wall
Dengan Ordinal Divisi
Ketika membagi dua nomor urut, di mana T adalah short, int, long, atau yang lain ordinal, pembulatan ekspresi ini.
T roundedQuotient = (2 * integerNumerator + 1)
/ (2 * integerDenominator);
Akurasi
Tidak ada keraguan bahwa aneh melihat ketidakakuratan muncul dalam operasi floating point, tapi ini hanya bila angka-angka yang muncul, dan memiliki sedikit untuk melakukan dengan pembulatan.
Sumber tidak hanya jumlah yang signifikan digit mantissa dari IEEE representasi floating point number, hal ini berkaitan dengan desimal berpikir sebagai manusia.
Sepuluh adalah produk dari lima dan dua, dan 5 dan 2 adalah relatif prima. Oleh karena itu IEEE floating point standar tidak mungkin dapat diwakili dengan sempurna sebagai angka desimal untuk semua digital biner representasi.
Ini bukan masalah dengan algoritma pembulatan. Ini adalah matematika kenyataan yang harus dipertimbangkan pada saat pemilihan tipe dan desain dari perhitungan, entri data, dan menampilkan angka. Jika sebuah aplikasi yang menampilkan angka yang menunjukkan desimal-biner masalah konversi, maka aplikasi ini secara visual mengekspresikan akurasi yang tidak ada di digital kenyataan dan harus diubah.
Jika anda perlu untuk dapat mengkompilasi kode di lingkungan yang mendukung C++11 standar, tetapi juga harus mampu untuk mengkompilasi kode yang sama di lingkungan yang don't dukungan itu, anda bisa menggunakan fungsi macro untuk memilih antara std::round() dan fungsi kustom untuk masing-masing sistem. Hanya lulus -DCPP11
atau /DCPP11
ke C++11-compliant compiler (atau menggunakan built-in versi macro), dan membuat 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, lihat http://ideone.com/zal709 .
Ini mendekati std::round() di lingkungan yang tidak't C++11-compliant, termasuk pelestarian sign bit untuk -0.0. Hal ini dapat menyebabkan sedikit performa, namun, dan kemungkinan besar akan memiliki masalah dengan pembulatan diketahui tertentu "masalah" floating-point nilai-nilai seperti 0.49999999999999994 atau nilai-nilai yang sama.
Atau, jika anda memiliki akses ke C++11-compliant compiler, anda hanya bisa ambil std::round() dari <cmath>
header, dan menggunakannya untuk membuat header sendiri yang mendefinisikan fungsi jika itu's belum didefinisikan. Catatan bahwa ini mungkin tidak menjadi solusi optimal, namun, terutama jika anda perlu untuk mengkompilasi untuk beberapa platform.
Berdasarkan Kalaxy's tanggapan, berikut ini adalah kerangka solusi yang setiap putaran angka floating point ke integer terdekat jenis berdasarkan alam pembulatan. Hal ini juga melempar kesalahan dalam mode debug jika nilai di luar jangkauan dari tipe integer, sehingga melayani kira-kira sebagai alternatif fungsi perpustakaan.
// 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);
}
Seperti yang ditunjukkan dalam komentar dan jawaban yang lain, ISO C++ standar library tidak menambahkan round()
sampai ISO C++11, ketika fungsi ini ditarik dengan mengacu pada ISO C99 matematika standar perpustakaan.
Untuk positif operan di [½, ub] round(x) == floor (x + 0.5)
, di mana ub 223 untuk float
ketika dipetakan ke IEEE-754 (2008) binary32
, dan 252 untuk double
ketika dipetakan ke IEEE-754 (2008) binary64
. Angka-angka 23 dan 52 sesuai dengan jumlah disimpan bit mantissa dalam dua floating-point format. Untuk positif operan di [+0, ½) round(x) == 0
, dan untuk positif operan dalam (ub, +∞] round(x) == x
. Sebagai fungsi adalah simetris terhadap sumbu-x negatif argumen x
dapat ditangani menurut bulat(-x) == -bulat(x)
.
Hal ini menyebabkan kompak kode di bawah ini. Mengkompilasi menjadi jumlah yang wajar dari instruksi mesin di berbagai platform. Saya mengamati yang paling kompak kode pada Gpu, di mana my_roundf()
membutuhkan sekitar selusin petunjuk. Tergantung pada arsitektur prosesor dan toolchain, ini floating-point berdasarkan pendekatan bisa menjadi lebih cepat atau lebih lambat dari bilangan bulat berbasis implementasi dari newlib dirujuk dalam jawaban yang berbeda.
Aku diuji my_roundf()
secara mendalam terhadap newlib roundf()
implementasi menggunakan Intel compiler versi 13, dengan kedua /fp:ketat
dan /fp:cepat
. Saya juga memeriksa bahwa newlib versi sesuai dengan roundf()
dalam mathimf
perpustakaan Intel compiler. Pengujian mendalam adalah tidak mungkin untuk double-precision round()
, namun kode ini secara struktural identik dengan single-precision implementasi.
#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;
}
Saya gunakan berikut pelaksanaan bulat di asm untuk arsitektur x86 dan MS VS tertentu C++:
__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;
}
Output:
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
Cara terbaik untuk pembulatan nilai mengambang oleh "n" desimal, adalah sebagai berikut dengan di O(1) waktu:-
Kita harus membulatkan nilai dengan 3 tempat yaitu n=3.Jadi,
float a=47.8732355;
printf("%.3f",a);
// 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");
Itu mungkin menjadi tidak efisien kotor cara konversi tapi sih, kerjanya lol. Dan itu's yang baik, karena itu berlaku untuk yang sebenarnya mengapung. Tidak hanya mempengaruhi output secara visual.