C ++ 'da float için round ()


232

Ben basit bir kayan nokta yuvarlama işlevi, böylece gerekir:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

Bulabilirim ceil()ve floor()math.h - ancak round().

Başka bir ad altında standart C ++ kitaplığında var mı, yoksa eksik mi ??


1
Sadece sayıyı yuvarlatılmış bir sayı olarak çıkarmak istiyorsanız std::cout << std::fixed << std::setprecision(0) << -0.9, örneğin bunu yapabilirsiniz .
Frank

44
Bunu koruyor ... Parlak yeni yuvarlama şemalarına sahip yeni kullanıcılar önce mevcut cevapları okumalıdır.
Shog9

12
roundC ++ 11 inç'ten beri kullanılabilir <cmath>. Ne yazık ki Microsoft Visual Studio'daysanız hala eksik: connect.microsoft.com/VisualStudio/feedback/details/775474/…
Alessandro Jacopson

3
Cevabımda belirttiğim gibi, kendinizin yuvarlanması roundçok fazla uyarı var. C ++ 11'den önce standart, içermeyen C90'a dayanıyordu round. C ++ 11, sahip olduğu roundfakat aynı zamanda belirttiğim gibi truncfarklı özelliklere sahip olan ve uygulamaya bağlı olarak daha uygun olabilen C99'a dayanır . Çoğu yanıt, bir kullanıcının daha fazla sorunu olan ayrılmaz bir tür döndürmek isteyebileceğini de göz ardı ediyor gibi görünmektedir.
Shafik Yaghmour

2
@uvts_cvs bu görsel stüdyonun en son sürümü ile ilgili bir sorun gibi görünmüyor, canlı olarak görün .
Shafik Yaghmour

Yanıtlar:


144

C ++ 98 standart kitaplığında round () yoktur. Yine de kendiniz yazabilirsiniz. Aşağıdakilerin bir yarısı kadar olan bir uygulamasıdır :

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

C ++ 98 standart kütüphanesinde yuvarlak bir fonksiyon olmamasının muhtemel nedeni, aslında farklı şekillerde uygulanabilmesidir. Yukarıdakiler yaygın bir yoldur, ancak daha az önyargılı olan ve genellikle çok fazla yuvarlama yapacaksanız daha iyi olan yuvarlak-eşit gibi diğerleri vardır ; gerçi uygulamak biraz daha karmaşık.


53
Bu, negatif sayıları doğru şekilde işlemez. Litb cevabı doğrudur.
Kayıtlı Kullanıcı

39
@InnerJoin: Evet, negatif sayıları litb'nin cevabından farklı olarak ele alır, ancak bu "yanlış" yapmaz.
Roddy

39
Kesmeden önce 0,5 ekleme, 0,999999999999999994 dahil olmak üzere birçok giriş için en yakın tam sayıya yuvarlanamaz. Bkz. Blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
Pascal Cuoq

10
@ Sergi0: "Doğru" ve "yanlış" yoktur, çünkü yarı yolda ne olacağına karar veren birden fazla yuvarlama tanımı vardır . Yargılamadan önce gerçeklerinizi kontrol edin.
Jon

16
@MuhammadAnnaqeeb: Haklısın, C ++ 11'in piyasaya sürülmesinden bu yana işler çok gelişti. Bu soru, hayatın zor ve sevinçlerin az olduğu başka bir zamanda soruldu ve cevaplandı. Burada, o zamanlar yaşayan ve savaşan kahramanlara ve hala modern araçları kullanamayan fakir ruhlara bir övgü olarak kalıyor.
Andreas Magnusson

96

Boost, basit bir yuvarlama işlevi seti sunar.

#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

Daha fazla bilgi için Boost belgelerine bakın .

Düzenleme : C ++ 11 olduğundan std::round, std::lroundvestd::llround .


2
Projemde zaten bunun için +1 kullanıyordum, saf floor(value + 0.5)yaklaşımı kullanmaktan çok daha iyi !
Gustavo Maciel

@GustavoMaciel Ben geç oyuna biraz biriyim ama boost uygulamasıdır floor(value + 0.5).
n. 'zamirler' m.

Aslında öyle değil: github.com/boostorg/math/blob/develop/include/boost/math/… 4 yıl sonra, floor(value + 0.5)bunun hiç naif olmadığını, aksine içeriğe ve doğaya bağlı olduğunu söylemek isterim Yuvarlamak istediğiniz değerlerin
Gustavo Maciel

84

C ++ 03 standardı, standardın C ++ 03 standardının taslağında (C ++ 03'e halka açık en yakın taslak standart N1804'tür ) Standart C Kütüphanesi dediği şey için C90 standardına dayanır. Normatif referanslar :1.2

ISO / IEC 9899: 1990 madde 7'de ve ISO / IEC 9899 / Amd.1: 1995 madde 7'de açıklanan kütüphane bundan böyle Standart C Kütüphanesi olarak adlandırılacaktır. 1)

Biz gidersek yuvarlak, lround C dokümantasyon, cppreference üzerinde llround biz görebilirsiniz yuvarlak ve ilgili fonksiyonlar parçası olan C99 ve böylece 03 veya öncesinde C ++ kullanılamayacaktır.

C ++ 11'de bu, C ++ 11'in C standart kitaplığı için C99 taslak standardına bağlı olması nedeniyle değişir ve bu nedenle std :: round ve integral dönüş türleri için std :: lround, std :: llround sağlar :

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

C99'dan başka bir seçenek de std :: trunc olacaktır :

Arg değerinden daha büyük olmayan en yakın tamsayıyı hesaplar.

#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 ;

}

C ++ 11 olmayan uygulamaları desteklemeniz gerekiyorsa, en iyi seçiminiz boost round, iround, lround, llround veya boost trunk kullanmak olacaktır .

Kendi raund versiyonunuzu yuvarlamak zordur

Kendinizi yuvarlamak , muhtemelen göründüğünden daha zor bir çabaya değmez : şamandırayı en yakın tam sayıya yuvarlama, bölüm 1 , Şamandırayı en yakın tam sayıya yuvarlama , bölüm 2 ve Yuvarlama şamandırasını en yakın tam sayıya yuvarlama, bölüm 3 açıklar:

Örneğin, uygulamanızı kullanan std::floorve ekleyen ortak bir rulo 0.5tüm girdiler için çalışmaz:

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

Bunun başarısız olacağı girişlerden biri 0.49999999999999994, ( canlı olarak görün ).

Diğer bir yaygın uygulama, bir kayan nokta tipinin bir integral tipine dökülmesini içerir; bu, integral parçasının hedef tipte temsil edilememesi durumunda tanımlanmamış davranışı uyarabilir. Bunu, taslak C ++ standart bölümü 4.9 Kayan-integral dönüşümlerden ( vurgu benim ) şöyle görebiliriz:

Kayan nokta türünün bir ön değeri, tamsayı türünün bir ön değerine dönüştürülebilir. Dönüşüm kısalır; yani kesirli kısım atılır. Kesilen değer hedef türünde gösterilemiyorsa, davranış tanımsızdır. [...]

Örneğin:

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

Verilen std::numeric_limits<unsigned int>::max()olduğu 4294967295şu çağrı sonra:

myround( 4294967296.5f ) 

taşmaya neden olur ( canlı olarak görün ).

C (round) () 'i uygulamanın Muhtasar yolunun bu cevabına bakarak bunun ne kadar zor olduğunu görebiliriz. tek hassas şamandıra turu newlibs sürümünü referans hangi . Basit görünen bir şey için çok uzun bir işlevdir. Kayan nokta uygulamaları hakkında bilgi sahibi olmayan herkesin bu işlevi doğru bir şekilde uygulayabilmesi olası görünmemektedir:

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

Öte yandan, diğer çözümlerin hiçbiri kullanılamıyorsa, newlib iyi test edilmiş bir uygulama olduğundan potansiyel olarak bir seçenek olabilir.


5
@downvoter neler geliştirilebileceğini açıklayınız? Buradaki cevabın büyük çoğunluğu yanlış, çünkü hepsi bir şekilde başarısız olan kendi turlarını yuvarlamaya çalışıyorlar. Açıklamamda eksik bir şey varsa lütfen bana bildirin.
Shafik Yaghmour

1
Güzel tam cevap - özellikle 0,5 bölümün hemen altında. Başka niş: round(-0.0). C spec belirtilmiş gibi görünmüyor. -0.0Sonuç olarak beklerdim .
chux - Monica'yı geri yükle

3
@chux ilginçtir ve IEEE 754-2008 standardı, yuvarlamanın sıfır ve sonsuzluk belirtilerini koruduğunu belirtir (bkz. 5.9).
Ruslan

1
@Shafik bu harika bir cevap. Yuvarlamanın bile önemsiz bir işlem olduğunu hiç düşünmemiştim.
Ruslan

1
Belki de sayısal ve performans nedenleriyle C ++ 11'in kullanılabilir std::rint()olduğu zaman tercih edilir std::round(). round()Özel modun aksine geçerli yuvarlama modunu kullanır . rintTek bir komutla satır içi olabilen x86'da çok daha verimli olabilir. (gcc ve clang , -ffast-math godbolt.org/g/5UsL2e olmadan bile bunu yaparken, sadece clang neredeyse eşdeğer olanı sıralarnearbyint() ) ARM için tek komut desteği vardır round(), ancak x86'da yalnızca birden fazla talimatla satır içi ve sadece-ffast-math
Peter Cordes

71

Yuvarlamadan tamsayı bir sonuç istiyorsanız, tavandan veya zeminden geçirmeniz gerekmediğini belirtmek gerekir. yani,

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

3
0.49999999999999994 için beklenen sonucu vermez (elbette ne beklediğinize bağlı olarak, ama 0 benim için 1'den daha makul görünüyor)
stijn

@stijn İyi yakaladım. Sabitlerime uzun ikili değişmez sonek eklemenin örnek sorununuzu çözdüğünü fark ettim, ancak yakalayamayacağı başka hassas örnekler olup olmadığını bilmiyorum.
kalaxy

1
btw, 0.5 yerine 0.49999999999999994 eklerseniz, hem 0.49999999999999994 hem de 5000000000000001.0 için giriş olarak tamam çalışır. Ancak tüm değerler için Tamam olup olmadığından emin değilim ve bu nihai düzeltme olduğunu belirten herhangi bir referans bulamadık.
stijn

1
@stijn İki tamsayı arasındaki değerlerin tam olarak hangi yönde yuvarlandığını umursamıyorsanız, tüm değerler için uygundur. Düşünmeden, bunu aşağıdaki durumlarla vaka analizi ile kanıtlarım: 0 <= d <0.5, 0.5 <= d <1.5, 1.5 <= d <2 ^ 52, d> = 2 ^ 52. Tek hassasiyetli kasayı da kapsamlı bir şekilde test ettim.
Pascal Cuoq

3
4.9 [conv.fpint] uyarınca, "Kesilen değer hedef türünde gösterilemiyorsa davranış tanımsızdır." , bu biraz tehlikeli. Diğer SO yanıtları bunun nasıl sağlam bir şekilde yapılacağını açıklar.
Tony Delroy

41

C ++ 11'den beri cmath ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf uyarınca )

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

Çıktı:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

1
ayrıca lroundve llroundintegral sonuçları için
sp2danny

@ sp2danny: ya da daha iyisi, sıfırdan uzakta eğik çizgi yerine lrintgeçerli yuvarlama modunu kullanmak içinround .
Peter Cordes

27

Genellikle olarak uygulanır floor(value + 0.5).

Düzenleme: ve bildiğim en az üç yuvarlama algoritması olduğundan muhtemelen yuvarlak olarak adlandırılmaz: sıfıra yuvarlama, en yakın tamsayıya yuvarlama ve bankacının yuvarlama. Yuvarlakdan en yakın tamsayıya soruyorsunuz.


1
Farklı 'yuvarlak' versiyonları arasında ayrım yapmak iyidir. Hangisini ne zaman seçeceğinizi bilmek güzel.
xtofl

5
Gerçekten de hepsi "doğru" olmak için makul iddialarda bulunabilecek farklı yuvarlama algoritmaları vardır. Ancak taban (değer + 0,5) bunlardan biri değildir. 0.49999997f veya eşdeğeri çift gibi bazı değerler için, cevap sadece yanlıştır - hepsi sıfır olması gerektiğine karar verdiğinde 1.0'a yuvarlanır. Ayrıntılar için bu gönderiye
Bruce Dawson

14

Baktığımız 2 sorun var:

  1. dönüşümleri yuvarlama
  2. dönüştürme türü.

Yuvarlama dönüşümleri yuvarlama ± şamandıra / çiftten en yakın kata / tavan şamandıra / çift demektir. Sorununuz burada bitiyor olabilir. Ancak, Int / Long döndürmeniz bekleniyorsa, tür dönüştürme gerçekleştirmeniz gerekir ve bu nedenle "Taşma" sorunu çözümünüze çarpabilir. Yani, fonksiyonunuzda bir hata kontrolü yapın

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

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


Kullanılması LONG_MIN-0.5ve LONG_MAX+0.5 matematik olarak tanıtır komplikasyonlar kesin olmayabilir. kesin dönüşüm hassasiyeti LONG_MAXaşabilir double. Bundan başka olası Talep assert(x < LONG_MAX+0.5); (vs <<=) olarak LONG_MAX+0.5tam olarak temsil edilebilir olabilir ve (x)+0.5tam bir sonucu olabilir LONG_MAX+1başarısız olan longdöküm. Diğer köşe sorunları da.
chux - Monica'yı geri yükle

İşlevinizi çağırma, round(double)zaten bu adın standart bir matematik kitaplığı işlevi (C ++ 11'de) var, bu yüzden kafa karıştırıcı. std::lrint(x)Varsa kullanın .
Peter Cordes

11

Boost'da belirli bir yuvarlama türü de uygulanır:

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

Bunun yalnızca tamsayıya tamsayı dönüşümü yaptığınızda işe yaradığını unutmayın.


2
Boost ayrıca bir dizi basit yuvarlama işlevi sunar; cevabımı gör.
Daniel Wolf

boost:numeric::RoundEven< double >::nearbyintTamsayı istemiyorsanız doğrudan da kullanabilirsiniz . Basit fonksiyon aka.nice tarafından dışarı koydu gibi sorunlara sahip olduğu +0.5 kullanılarak uygulandığını @DanielWolf notu
stijn

6

Aşağıdakileri kullanarak n basamak hassasiyetine yuvarlayabilirsiniz:

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

4
Derleyici int büyüklüğünüz varsayılan olarak 1024 bit olarak
ayarlanmadığı

Sanırım ne zaman kullanılacağını kabul edilebilir: Eğer çift değeriniz 1,0 e + 19 ise, 3 yere yuvarlamak mantıklı değil.
Carl

3
emin, ama soru genel bir tur için ve nasıl kullanılacağını kontrol edemezsiniz. Tavanın ve zeminin durmayacağı yerde turun başarısız olması için hiçbir neden yoktur.
aka.nice

Bu, aralığının dışındaki argümanlar için tanımlanmamış bir davranışa sahiptir int. (X86 Uygulamada, dışarı aralık FP değerleri yapacak CVTTSD2SIüretmek0x80000000 yani tamsayı bit deseni gibi INT_MINdaha sonra dönüştürülmüş geri dönecek, double.
Peter Cordes

5

Bu günlerde C99 / C ++ 11 matematik kütüphanesini içeren bir C ++ 11 derleyicisi kullanmak sorun olmamalı. Ama sonra soru şu olur: Hangi yuvarlama işlevini seçersiniz?

C99 / C ++ 11 round()genellikle istediğiniz yuvarlama işlevi değildir . Yarım yolda kalan durumlarda ( +-xxx.5000) bir mola olarak 0'dan uzaklaşan korkak bir yuvarlama modu kullanır . Özellikle yuvarlama modunu istiyorsanız veya round()daha hızlı bir yerde bir C ++ uygulaması hedefliyorsanız rint(), onu kullanın (veya davranışını bu sorudaki yüz değerine götüren ve dikkatlice yeniden üreten diğer sorulardan biriyle taklit edin. yuvarlama davranışı.)

round()'nin yuvarlaması, bir tie-break olarak bile IEEE754 varsayılan yuvarlamadan en yakın moda farklıdır . En yakın, hatta ortalama sayı büyüklüğünde istatistiksel yanlılığı önler, ancak eşit sayılara karşı önyargı yapar.

Geçerli varsayılan yuvarlama modunu kullanan iki matematik kitaplığı yuvarlama işlevi vardır: std::nearbyint()ve std::rint()her ikisi de C99 / C ++ 11'de eklenmiştir, bu nedenle her zaman std::round()kullanılabilirler. Tek fark, nearbyintFE_INEXACT'i asla yükseltmemesidir.

rint()Performans nedenlerinden dolayı tercih edin : gcc ve clang hem daha kolay satır içi, ancak gcc asla satır içi nearbyint()(hatta ile -ffast-math)


x86-64 ve AArch64 için gcc / clang

Matt Godbolt'un Derleyici Gezgini'ne kaynak + asm çıktısını görebileceğiniz bazı test işlevleri koydum (birden fazla derleyici için). Derleyici çıktısını okuma hakkında daha fazla bilgi için bu soru-cevap bölümüne ve Matt'in CppCon2017 konuşmasına bakın: “Derleyicim Son zamanlarda Benim İçin Ne Yaptı? Derleyicinin Kapağının Açılması ” ,

FP kodunda, küçük işlevleri satır içine almak genellikle büyük bir kazançtır. Özellikle, standart arama kuralında arama korumalı kayıtların bulunmadığı Windows olmayanlarda, bu nedenle derleyici, a üzerinden XMM kayıtlarında herhangi bir FP değeri tutamaz call. Yani gerçekten bilmiyor olsanız bile, bunun kütüphane işlevine sadece bir kuyruk çağrısı olup olmadığını veya bir veya iki matematik komutuna satırlı olup olmadığını kolayca görebilirsiniz. Bir veya iki talimatın içinde bulunan her şey bir işlev çağrısından daha iyidir (x86 veya ARM'deki bu özel görev için).

X86'da, SSE4.1'e satır içi olan her şey roundsdSSE4.1 roundpd(veya AVX vroundpd) ile otomatik olarak vektörleştirilebilir . (FP-> tamsayı dönüşümleri, AVX512 gerektiren FP-> 64-bit tamsayı hariç, paketlenmiş SIMD biçiminde de mevcuttur.)

  • std::nearbyint():

    • x86 clang: ile tek bir insn için satırlar -msse4.1.
    • x86 gcc: -msse4.1 -ffast-mathyalnızca gcc 5.4 ve önceki sürümlerinde ve yalnızca gcc'de tek bir insn için satırlar . O (belki onlar hatalı istisna bastırmak acil bit Şunu fark etmedi en neyi çınlama kullanır, ancak eski gcc aynı acil gelince kullanılıp kullanılmadığı? İnlines Daha sonra asla gcc'ye rinto satır içi yaptığında o)
    • AArch64 gcc6.3: varsayılan olarak tek bir insn için satırlar.
  • std::rint:

    • x86 clang: ile tek bir insn satır içi -msse4.1
    • x86 gcc7: ile tek bir insn için satırlar -msse4.1. (SSE4.1 olmadan, birkaç talimatın satır içi)
    • x86 gcc6.x ve öncesi: ile tek bir insn için satırlar -ffast-math -msse4.1.
    • AArch64 gcc: varsayılan olarak tek bir insn için satırlar
  • std::round:

    • x86 clang: satır içi değil
    • x86 gcc: ile -ffast-math -msse4.1iki vektör sabitini gerektiren birden çok yönerge için satırlar .
    • AArch64 gcc: tek bir talimatı gösterir (bu yuvarlama modu için HW desteği, IEEE varsayılanı ve diğerleri).
  • std::floor/ std::ceil/std::trunc

    • x86 clang: ile tek bir insn satır içi -msse4.1
    • x86 gcc7.x: ile tek bir insn için satırlar -msse4.1
    • x86 gcc6.x ve öncesi: ile tek bir insn için satırlar -ffast-math -msse4.1
    • AArch64 gcc: varsayılan olarak tek bir komutla satır içi

int/ long/ Yuvarlama long long:

Kullanımı: İki burada seçenek var lrint(gibi rintama döner long, ya long longiçin llrint), ya da (kesikleri ile) Normal şekilde yazmak işlevini yuvarlama bir FP-> korunmak ve daha sonra tam sayıya dönüştürmek. Bazı derleyiciler biri diğerinden daha iyi optimize eder.

long l = lrint(x);

int  i = (int)rint(x);

O Not int i = lrint(x)dönüştürür floatveya double> - longÖnce ve sonra için tamsayı keser int. Bu, aralık dışı tamsayılar için bir fark yaratır: C ++ 'da Tanımsız Davranış, ancak x86 FP -> int talimatları için iyi tanımlanmış (derleyici, sabit yayılım yaparken UB'yi derleme zamanında görmedikçe yayımlayacaktır, o zaman yürütülürse kırılan kod oluşturmasına izin verilir).

X86'da, tamsayı taşan bir FP-> tamsayı dönüşümü üretir INT_MINveya LLONG_MIN(yalnızca bit biti ile bit biti 0x8000000veya 64 bit eşdeğeri). Intel bunu "tamsayı belirsiz" değer olarak adlandırır. (Bkz manuel giriş , O ile mevcut olduğunu kesikleri ile çevirir () imzalı tamsayı sayıl çift. SSE2 talimat 32-bit veya 64-bit tamsayı hedef (64-bit modunda kullanılabilir). Bir de var şimdiki yuvarlama ile (dönüştürme modu), derleyicinin yaymasını istediğimiz şeydir, ancak ne yazık ki gcc ve clang bunu olmadan yapmaz .cvttsd2sicvtsd2si-ffast-math

Ayrıca unsignedint / long'a / den FP'nin x86'da (AVX512 olmadan) daha az verimli olduğuna dikkat edin . 64 bit makinede imzasız 32 bit'e dönüştürme oldukça ucuzdur; sadece 64-bit imzalı dönüştürün ve kısaltın. Ancak aksi takdirde önemli ölçüde yavaştır.

  • x86 clang içeren / içermeyen -ffast-math -msse4.1: (int/long)rintsatır içi roundsd/ ile cvttsd2si. (optimizasyonu kaçırdı cvtsd2si). lrinthiç satır içi değil.

  • x86 gcc6.x ve öncesi olmadan -ffast-math: hiçbir şekilde satır içi

  • x86 gcc7 olmadan -ffast-math: (int/long)rintayrı ayrı yuvarlar ve dönüştürür ( SSE4.1 toplam 2 talimatı etkinleştirilir, aksi takdirde bir grup kod rintolmadan olmadan satırlanır roundsd). lrintsatır içi değil.
  • x86 gcc ile -ffast-math : satır içi cvtsd2si(optimal) tüm yollar , SSE4.1 gerekmez.

  • AArch64 gcc6.3 olmadan -ffast-math: (int/long)rint2 talimatı gösterir. lrintsatır içi değil

  • AArch64 gcc6.3 with -ffast-math: (int/long)rintçağrısını derler lrint. lrintsatır içi değil. Olmadan aldığımız iki talimat -ffast-mathçok yavaş olmadığı sürece bu kaçırılan bir optimizasyon olabilir .

YAPILACAKLAR: ICC ve MSVC Godbolt'ta da mevcut, ancak bunun çıktılarına bakmadım. editörler hoş geldiniz ... Ayrıca: önce derleyici / sürüm ve daha sonra bu işlev ile ayrılmak daha yararlı olur mu? Çoğu insan derleyicileri FP-> FP veya FP-> tamsayı yuvarlamalarını ne kadar iyi derlediklerine göre değiştirmez.
Peter Cordes

2
rint()Bu durumun uygulanabilir bir seçim olduğunu önermek için +1 , genellikle durum böyle. Sanırım adı round()bazı programcılara rint()gizemli görünse de istedikleri şey budur . Bunun round()"korkak" bir yuvarlama modu kullanmadığına dikkat edin: en yakın bağları yuvarlamak resmi bir IEEE-754 (2008) yuvarlama modudur. Büyük nearbyint()ölçüde aynı olduğu rint()ve koşullar altında özdeş olması gerektiği gibi , satır içine girmemesi merak ediyor -ffast-math. Bu benim için böcek gibi görünüyor.
njuffa

4

Dikkat edin floor(x+0.5). [2 ^ 52,2 ^ 53] aralığındaki tek sayılar için neler olabileceği aşağıda açıklanmıştır:

-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

Bu http://bugs.squeak.org/view.php?id=7134 . @Konik gibi bir çözüm kullanın.

Kendi sağlam sürümüm şöyle olurdu:

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

Zeminden (x + 0,5) kaçınmanın bir başka nedeni burada verilmiştir .


2
Downvotes hakkında bilmek istiyorum. Kravat en yakın çift yerine sıfırdan çözüldüğü için mi?
aka.nice

1
Not: C spec "geçerli yuvarlama yönüne bakılmaksızın, yarım vakaları sıfırdan uzağa yuvarlama" diyor, bu nedenle tek / çift dikkate alınmadan yuvarlama uyumludur.
chux - Monica'yı geri yükle

4

Sonunda işlevinizin doubleçıktısını bir değerine dönüştürmek istiyorsanız , bu sorunun kabul edilen çözümleri aşağıdaki gibi görünecektir:round()int

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

Bu , homojen rasgele değerlerde geçtiğinde makinemde yaklaşık 8.88 ns hızında saatler .

Aşağıdakiler, işlevsel olarak eşdeğer, anlayabildiğim kadarıyla, ancak önemli bir performans avantajı için makinemde 2.48 ns hızında saatler var :

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

Daha iyi performansın nedenleri arasında atlanan dallanma vardır.


Bu, aralığının dışındaki argümanlar için tanımlanmamış bir davranışa sahiptir int. (X86 Uygulamada, dışarı aralık FP değerleri yapacak CVTTSD2SIüretmek0x80000000 yani tamsayı bit deseni gibi INT_MINdaha sonra dönüştürülmüş geri dönecek, double.
Peter Cordes

2

Hiçbir şey uygulamaya gerek yoktur, bu yüzden bu kadar çok cevabın neden tanımları, işlevleri veya yöntemleri içerdiğinden emin değilim.

C99'da

Tip-genel makrolar için aşağıdakilere ve <tgmath.h> başlıklarına sahibiz.

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

Bunu derleyemiyorsanız, muhtemelen matematik kütüphanesini dışarıda bırakmışsınızdır. Buna benzer bir komut sahip olduğum her C derleyicisinde çalışır (birkaç).

gcc -lm -std=c99 ...

C ++ 11'de

#İnclude <cmath> 'de IEEE çift kesinlikli kayan noktaya dayanan aşağıdaki ve ek aşırı yüklemelere sahibiz.

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

Std ad alanında da eşdeğerler vardır .

Bunu derleyemiyorsanız, C ++ yerine C derlemesi kullanıyor olabilirsiniz. Aşağıdaki temel komut g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0 ve Visual C ++ 2015 Topluluğu ile ne hata ne de uyarı üretiyor.

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

Ordinal Bölümü ile

T'nin kısa, int, uzun veya başka bir ordinal olduğu iki sıra sayısını bölerken, yuvarlama ifadesi budur.

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

doğruluk

Hiç şüphe yok ki kayan nokta işlemlerinde garip görünümlü yanlışlıklar ortaya çıkıyor, ancak bu sadece sayılar göründüğünde ve yuvarlama ile çok az ilgisi olduğunda.

Kaynak sadece kayan nokta sayısının IEEE temsilinin mantisindeki önemli basamakların sayısı değil, aynı zamanda insanlar olarak ondalık düşüncemizle ilgilidir.

On, beş ve ikinin ürünüdür ve 5 ve 2 nispeten asaldır. Bu nedenle, IEEE kayan nokta standartları muhtemelen tüm ikili dijital gösterimler için ondalık sayılar olarak mükemmel bir şekilde temsil edilemez.

Bu, yuvarlama algoritmalarıyla ilgili bir sorun değildir. Türlerin seçimi ve hesaplamaların tasarımı, veri girişi ve sayıların gösterilmesi sırasında dikkate alınması gereken matematiksel gerçekliktir. Bir uygulama bu ondalık ikili dönüşüm sorunlarını gösteren rakamları görüntülüyorsa, uygulama dijital gerçeklikte bulunmayan ve değiştirilmesi gereken doğruluğu görsel olarak ifade ediyor demektir.


1
"Bu kadar çok cevabın neden tanımları, işlevleri veya yöntemleri içerdiğinden emin değilim." Ne zaman istendiğine bir göz atın - C ++ 11 henüz dışarı çıkmadı. ;)
jaggedSpire

@jaggedSpire, uygun olduğunu düşünüyorsanız bana bir başparmak verin, çünkü tüm yüksek puanlı cevaplar günümüzün en yaygın kullanılan derleyicileri bağlamında eski ve yanıltıcıdır.
FauChristian

2

Fonksiyon double round(double)kullanımı ile modffonksiyon:

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

Temiz bir derleme yapabilmek için "math.h" ve "limitler" gereklidir. İşlev aşağıdaki yuvarlama şemasına göre çalışır:

  • 5.0'ın turu 5.0
  • 3.8'lik tur 4.0
  • 2.3 turu 2.0
  • 1.5'in turu 2.0
  • 0.501 turu 1.0
  • 0,5 tur 1,0
  • 0.499 yuvarlak 0.0
  • 0,01 turu 0,0
  • 0.0 turu 0.0
  • -0.01 turu -0.0
  • -0.499 turu -0.0
  • -0.5 turu -0.0
  • -0.501'in turu -1.0
  • -1.5 tur -1.0
  • -2.3 turu -2.0
  • -3.8 turu -4.0
  • -5.0 raundu -5.0

2
Bu iyi bir çözüm. -1.5 ile -1.0 arasında yuvarlamanın standart olduğundan emin değilim, simetri ile -2.0 beklenir. Ayrıca önde gelen nöbetçinin noktasını da görmüyorum, eğer ilk ikisi çıkarılabilirse.
aka.nice

2
ISO / IEC 10967-2 standardını kontrol ettim, open-std.org/jtc1/sc22/wg11/docs/n462.pdf ve ek B.5.2.4'ten yuvarlama işlevi gerçekten simetrik olmalı, yuvarlama_F (x) = neg_F (rounding_F (neg_F (x)))
aka.nice

Bu yavaş C ++ 11 oranla olacak rint()ya nearbyint(), ama sen gerçekten uygun bir yuvarlama fonksiyonu sağlayan bir derleyici kullanamazsınız, gelip ... daha performanstan daha hassas gerekiyorsa
Peter Cordes

1

C ++ 11 standardını destekleyen ortamlarda kod derlemeniz gerekiyorsa, ancak aynı kodu desteklemeyen ortamlarda da derleyebilmeniz gerekiyorsa, std arasında seçim yapmak için bir işlev makrosu kullanabilirsiniz. :: round () ve her sistem için özel bir işlev. Sadece -DCPP11veya /DCPP11C ++ 11 uyumlu derleyiciye geçin (veya yerleşik sürüm makrolarını kullanın) ve aşağıdaki gibi bir başlık oluşturun:

// 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 */

Hızlı bir örnek için bkz. Http://ideone.com/zal709 .

Bu, -0.0 için işaret bitinin korunması da dahil olmak üzere C ++ 11 uyumlu olmayan ortamlarda std :: round () değerine yakındır. Bununla birlikte, hafif bir performans vuruşuna neden olabilir ve muhtemelen 0.4999999999999999994 veya benzer değerler gibi bilinen bazı "sorun" kayan nokta değerlerinin yuvarlanmasıyla ilgili sorunlar yaşayacaktır.

Alternatif olarak, bir C ++ 11 uyumlu derleyiciye erişiminiz varsa, <cmath>başlığından std :: round () alabilir ve daha önce tanımlanmamışsa işlevi tanımlayan kendi başlığınızı oluşturmak için kullanabilirsiniz. Bununla birlikte, özellikle birden çok platform için derlemeniz gerekiyorsa, bunun en uygun çözüm olmayabileceğini unutmayın.


1

Kalaxy'nin cevabına dayanarak, aşağıdakiler herhangi bir kayan nokta sayısını doğal yuvarlamaya dayalı en yakın tamsayı tipine yuvarlayan şablonlu bir çözümdür. Değer tamsayı türünün kapsamı dışındaysa hata ayıklama modunda da bir hata atar ve böylece kabaca geçerli bir kitaplık işlevi olarak işlev görür.

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

1
Ben de belirttiği gibi cevabım ekleyerek 0.5her durumda çalışmaz. En azından taşma sorunu ile uğraşmanıza rağmen tanımsız davranışlardan kaçının.
Shafik Yaghmour

1

Yorumlarda ve diğer yanıtlarda belirtildiği gibi, round()bu işlev ISO C99 standart matematik kitaplığına referansla çekildiğinde , ISO C ++ standart kitaplığı ISO C ++ 11'e kadar eklenmedi.

Pozitif işlenen [½ için UB ] round(x) == floor (x + 0.5), ub 2 23 için floatIEEE-754 (2008) eşlenmiş zaman binary32, ve 2 52 için doubleIEEE-754 (2008) eşlenir zaman binary64. 23 ve 52 sayıları bu iki kayan noktalı formatta depolanan mantis bitlerinin sayısına karşılık gelir . [+0, ½) round(x) == 0içindeki pozitif işlenenler ve ( ub , + ∞] içindeki pozitif işlenenler için round(x) == x. Fonksiyon x ekseni etrafında simetrik olduğu için, negatif argümanlar xele alınabilir round(-x) == -round(x).

Bu, aşağıdaki kompakt koda yol açar. Çeşitli platformlarda makul sayıda makine talimatı derler. my_roundf()Yaklaşık bir düzine talimat gerektiren GPU'larda en kompakt kodu gözlemledim . İşlemci mimarisine ve araç zincirine bağlı olarak, bu kayan nokta tabanlı yaklaşım, farklı bir yanıtta başvurulan newlib'in tamsayı tabanlı uygulamasından daha hızlı veya daha yavaş olabilir .

Ben test my_roundf()newlib karşı etraflıca roundf()hem Intel derleyici sürümü 13 kullanılarak uygulanması, /fp:strictve /fp:fast. Ben de newlib versiyonu eşleştiğini kontrol roundf()içinde mathimfIntel derleyici kütüphanesine. Çift kesinlik için kapsamlı testler mümkün değildir round(), ancak kod yapısal olarak tek kesinlikli uygulama ile aynıdır.

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

Ben int16 bit daha geniş olduğunu varsayalım önlemek için bir düzenleme yaptım . Elbette float4 baytlık IEEE754 ikili olduğunu varsayıyor32. Bir C ++ 11 static_assertveya belki bir makro #ifdef/ #errorbunu kontrol edebilir. (Ama elbette C ++ 11 mevcutsa, gcc ve clang ile güzel bir şekilde hizalanan std::roundgeçerli yuvarlama modu kullanımı için kullanmalısınız std::rint).
Peter Cordes

BTW, a ve ardından a gcc -ffast-math -msse4.1satırlarına std::round()gelir . bunu oldukça verimli uygular, yani açısından . Eğer varsa çünkü C ++ kaynağında el ile yapmak için bir neden, yok ya da var . Bir godbolt bağlantısı ve farklı gcc / clang sürümleri ile satır içi veya olmayan satırların bir özeti için cevabımı görün. add( AND(x, L1), OR(x,L2)roundsdroundrintstd::rint()std::nearbyint()std::round()
Peter Cordes

@PeterCordes round()Verimli olarak nasıl uygulanacağının iyi farkındayım rint()(ikincisi, en yakın ya da hatta modda çalışırken): Bunu CUDA standart matematik kütüphanesi için uyguladım. Ancak, bu soru round()C ++ 11'den önce C ++ ile nasıl uygulanacağını soruyor gibi görünüyordu, bu yüzden rint()sadece floor()ve mevcut olmayacaktı ceil().
njuffa

@PeterCordes Üzgünüm, yanlış yazdım. round()Kolayca sentezlenir rint()içinde yuvarlak sıfıra aka mod trunc(). İlk kahveden önce cevap vermemeliydim.
njuffa

1
@PeterCordes OP'nin belirli yuvarlama davranışına ihtiyaç duymadığını kabul ediyorum round(); En programcılar sadece arasındaki ayrımın farkında değildir round()vs rint()ile yuvarlak arası en yakın bile, ikincisi genellikle donanım ve dolayısıyla daha verimli doğrudan temin edilir; CUDA Programlama Kılavuzu'nda programcıların farkında olmasını sağlamak için bunu söyledim: "Tek duyarlıklı kayar nokta işlenenini bir tamsayıya yuvarlamanın önerilen yolu, sonuç tek duyarlıklı kayan nokta sayısıdır rintf(), değil roundf()".
17:32

0

Ben x86 mimarisi ve MS VS özel C ++ için asm aşağıdaki yuvarlak uygulamasını kullanın:

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

UPD: çift değer döndürmek için

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

Çıktı:

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

Sonuç değeri kayar nokta değeri çift kesinlikli olmalıdır.
truthseeker

@ truthseeker: Evet, gerekli dönüş değeri türünü görmek zorundaydım. Tamam, bkz. "UPD".
Aleksey F.

Derleyici umarım inline olacak rint()ya nearbyint()bir SSE4.1 için roundsdtalimat veya x87 frndintçok daha hızlı bir kayıtta verilerin bu satır içi asm kullanmak için gereken iki mağaza / yeniden yuvarlak gezileri daha olacak talimat. MSVC satır içi asm frndint, bir kayıtta girdi almanın bir yolu olmadığından, tek talimatları sarmak için oldukça berbat . Sonuç ile sonuçlanan bir fonksiyonun sonunda kullanılması, st(0)çıktıyı döndürmenin bir yolu olarak güvenilir olabilir; Görünüşe göre eaxasm içeren işlevi satır içine aldığında bile tamsayılar için güvenlidir .
Peter Cordes

@PeterCordes Modern optimizasyonlara açıktır. Ancak o anda mevcut olmadığı için SSE4.1'i kullanamadım. Amacım, 2000'lerden kalma eski Intel P3 veya P4 ailelerinde bile çalışabilecek minimum tur uygulamasını sağlamaktı.
Aleksey F.

P3'ün SSE2'si bile yok, bu yüzden derleyici zaten x87'yi kullanacak doubleve bu nedenle frndintkendini yayabilmelidir rint(). Derleyiciniz SSE2 kullanıyorsa, doublebir XMM kaydından x87'ye ve geri atlamak buna değmeyebilir.
Peter Cordes

0

Kayan bir değeri "n" ondalık basamakla yuvarlamanın en iyi yolu, O (1) zamanında aşağıdaki gibidir: -

Değeri 3 yerle yuvarlamalıyız, yani n = 3.

float a=47.8732355;
printf("%.3f",a);

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

Dönüşümün verimsiz bir kirli yolu olabilir ama halt, lol çalışır. Ve bu iyi, çünkü gerçek şamandıra için geçerlidir. Sadece çıktıyı görsel olarak etkilemekle kalmaz.


Bu komik bir şekilde verimsizdir ve ayrıca en yakınına yuvarlamak yerine (her zaman arkadaki rakamları atarak) kısalır.
Peter Cordes

-6

Bunu ben yaptım:

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

3
Şunu mu demek istediniz: ikili operatör ^ yerine pow (10, place) ^ in 10 ^ place? Makinemdeki 10 ^ 2 bana 8 verir! Yine de Mac 10.7.4 ve gcc'de, kod orijinal değeri döndürerek çalışmaz.
Pete855217
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.