Cout kullanarak çift değeri tam hassasiyetle nasıl yazdırabilirim?


332

Son sorumun cevabını aldım (neden böyle düşünmediğimi bilmiyorum). Bir basıyordum doublekullanarak coutbunu beklemiyordum zaman anladınız yuvarlanır. Nasıl coutbaskı yapabilirimdoubleTam hassasiyet kullanarak ?

Yanıtlar:


391

Hassasiyeti doğrudan ayarlayabilir std::coutve std::fixedformat belirleyiciyi kullanabilirsiniz .

double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;

#include <limits>Bir şamandıra veya çiftin maksimum hassasiyetini elde edebilirsiniz .

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;

46
Neden açıkça kullanılmasını öneriyorsunuz fixed? İle double h = 6.62606957e-34;, fixedbana verir 0.000000000000000ve scientificçıktılar 6.626069570000000e-34.
Arthur

36
Hassasiyet 17 (veya std :: numeric_limits <double> :: digits10 + 2) olmalıdır, çünkü değerin aynı orijinal değere yuvarlandığından emin olmak için ondalık basamaktan ikili gösterime dönüştürülürken 2 ekstra basamak gereklidir. İşte bazı detayları içeren bir makale: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher

8
Gerçekten doğru cevap mı? Manuel olarak yüksek bir sayı kullandığımda, yaklaşık 51 basamaklı yaklaşık e basabilirim, ancak cout.precision(numeric_limits<double>::digits10 + 2);sadece 16'yı alıyorum ....
Assimilater

6
@MikeFisher'ın alıntı yaptığı makalede 17 basamaktan bahsettiği yerleri arayanlar, Teorem 15 altındadır.
Emile Cormier

15
@MikeFisher Haklısın, C ++ 11max_digits10 aynısını göstermek için tanıtıldı . Yanıtın bunu yansıtması düzeltildi.
legends2k

70

Kullanım std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;

2
Bir tür MAX_PRECISION makro veya enum ya da std :: setPrecision'a iletebileceğim bir şey var mı?
Jason Punyon

2
std :: bir çift (ok veya 16) için setprecision (15), log_10 (2 ** 53) ~ = 15.9
user7116

14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant

6
Olmalı std::setprecision (17)çift için, @Bill Lizard'ın cevap yorumlara bakınız.
Alec Jacobson

9
std :: setprecision'ın çalışması için #include <iomanip> dahil edilmelidir.
user2262504

24

İşte ne kullanacağım:

std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
          << 3.14159265358979
          << std::endl;

Temel olarak sınırlar paketi, tüm yapı türleri için özelliklere sahiptir.
Kayan nokta sayılarının özelliklerinden biri (float / double / long double) digits10 özelliğidir. Bu, taban 10'daki bir kayan nokta sayısının doğruluğunu (kesin terminolojiyi unutuyorum) tanımlar.

Bkz. Http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Diğer özellikler hakkında ayrıntılar için.


12
Bu başlık aşağıdakileri kullanmak için gereklidir std::setprecision(): #include <iomanip>
Martin Berger

std::numeric_limits<double>yerine olmalınumberic_limits<double>
niklasfi

2
Neden eklerim 1için std::numeric_limits<double>::digits10?
Alessandro Jacopson

5
@LokiAstari Bunun max_digits10yerine C + 11'leri kullanabilirsiniz . Bkz bu .
legends2k

1
@AlecJacobson Olması gerekir max_digits10, keyfi değil digits10+2. Aksi takdirde, söz konusu float, long double, boost::multiprecision::float128, gerekir orada beri bu, başarısız olacaktır +3yerine +2.
Ruslan

14

İostreams yolu biraz tıknaz. Kullanmayı tercih ederim boost::lexical_castçünkü benim için doğru hassasiyeti hesaplıyor. Ve hızlı da.

#include <string>
#include <boost/lexical_cast.hpp>

using boost::lexical_cast;
using std::string;

double d = 3.14159265358979;
cout << "Pi: " << lexical_cast<string>(d) << endl;

Çıktı:

Pi: 3.14159265358979


Destek belgeleri, "std :: numeric_limits'e karşılık gelen uzmanlığa sahip sayısallar için, geçerli sürüm artık eşleşecek bir hassasiyet seçiyor" diyor. Bu, maksimum hassasiyeti elde etmenin en kolay yolu gibi görünüyor. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo

11

Tam hassasiyetle, amaçlanan değere en iyi yaklaşımı gösterecek kadar hassas olduğunu varsayıyorum, ancak doublebaz 2 temsili kullanılarak saklanan ve baz 2'nin 1.1tam olarak önemsiz bir şeyi temsil edemeyeceği belirtilmelidir . Gerçek çiftin tam hassasiyetini elde etmenin tek yolu (YUVARLAK KAPALI HATALI), ikili bitleri (veya altıgen nybbles) yazdırmaktır. Bunu yapmanın bir yolu, yazıyor doublebir etmek unionve daha sonra bit tamsayı değeri yazdırmak.

union {
    double d;
    uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;

Bu size çiftin% 100 doğru hassasiyetini verecektir ... ve tamamen okunamaz olacaktır çünkü insanlar IEEE çift formatını okuyamaz! Wikipedia , ikili bitlerin nasıl yorumlanacağı konusunda iyi bir yazıya sahiptir.

Daha yeni C ++ 'da şunları yapabilirsiniz:

std::cout << std::hexfloat << 1.1;

10

Bir çiftin tam hassasiyetle nasıl gösterileceği aşağıda açıklanmıştır:

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;

Bu görüntülenir:

100,0000000000005


max_digits10, tüm farklı çift değerleri benzersiz bir şekilde temsil etmek için gereken basamak sayısıdır. max_digits10, ondalık basamaktan önceki ve sonraki basamak sayısını temsil eder.


Std :: fixed ile set_precision (max_digits10) kullanmayın.
Sabit gösterimde, set_precision () yalnızca ondalık noktadan sonraki basamak sayısını ayarlar . Max_digits10 , ondalık noktadan önceki ve sonraki basamak sayısını temsil ettiğinden bu yanlıştır .

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;

Bu yanlış sonuç gösteriyor:

100,00000000000049738

Not: Başlık dosyaları gerekir

#include <iomanip>
#include <limits>

4
Bu 100.0000000000005, tam olarak bir olarak gösterilmediği için olur double. (Olması gerektiği gibi görünebilir, ancak normalleşmez , yani ikili temsili). Bunu görmek için, deneyin: 100.0000000000005 - 100. Anlıyoruz 4.973799150320701e-13.
Evgeni Sergeev

9

Bir doubledeğeri cout kullanarak tam hassasiyetle nasıl yazdırabilirim ?

Hassasiyeti kullanın hexfloatveya
kullanın scientificve ayarlayın

std::cout.precision(std::numeric_limits<double>::max_digits10 - 1);
std::cout << std::scientific <<  1.0/7.0 << '\n';

// C++11 Typical output
1.4285714285714285e-01

Çok fazla cevap 1) temel 2) sabit / bilimsel düzen veya 3) hassasiyetten sadece birini ele almaktadır. Hassaslıkla çok fazla cevap gerekli olan uygun değeri sağlamaz. Bu yüzden eski bir sorunun cevabı.

  1. Ne üssü?

A doublekesinlikle taban 2 kullanılarak kodlanır. C ++ 11 ile doğrudan bir yaklaşım kullanarak yazdırmaktır std::hexfloat.
Ondalık olmayan bir çıktı kabul edilebilirse, işimiz bitmiştir.

std::cout << "hexfloat: " << std::hexfloat << exp (-100) << '\n';
std::cout << "hexfloat: " << std::hexfloat << exp (+100) << '\n';
// output
hexfloat: 0x1.a8c1f14e2af5dp-145
hexfloat: 0x1.3494a9b171bf5p+144

  1. Aksi takdirde: fixedveya scientific?

A double, kayan nokta tipidir, sabit nokta değildir .

Do not kullanmak std::fixedbu küçük yazdırmak için başarısız olarak doublebir şey ama 0.000...000. Büyük için double, birçok basamak, belki de yüzlerce şüpheli bilgilendirme basar .

std::cout << "std::fixed: " << std::fixed << exp (-100) << '\n';
std::cout << "std::fixed: " << std::fixed << exp (+100) << '\n';
// output
std::fixed: 0.000000
std::fixed: 26881171418161356094253400435962903554686976.000000 

Tam hassasiyetle yazdırmak için ilk önce std::scientific"bilimsel gösterimde kayan nokta değerleri yazacak" kullanın. Ondalık noktadan sonraki 6 basamaklı varsayılan değerin, yetersiz bir miktarın bir sonraki noktada ele alındığına dikkat edin.

std::cout << "std::scientific: " << std::scientific << exp (-100) << '\n';  
std::cout << "std::scientific: " << std::scientific << exp (+100) << '\n';
// output
std::scientific: 3.720076e-44
std::scientific: 2.688117e+43

  1. Ne kadar hassasiyet (toplam toplam basamak)?

Bir doubleikili tabanı 2 kodlamakta Bu genellikle 53 bit 2. çeşitli güçler arasında aynı hassas kullanılarak kodlanır.

[1.0 ... 2.0) 2 53 farklı double,
[2.0 ... 4.0) 2 53 farklı double,
[4.0 ... 8.0) 2 53 farklı double,
[8.0 ... 10.0) 2 / 8 * 2 53 farklı double.

Yine de, kod Nönemli basamaklarla ondalık olarak yazdırılıyorsa , kombinasyon sayısı [1.0 ... 10.0) 9/10 * 10 N'dir .

NSeçilen (kesinlik) ne olursa olsun , doubleondalık metin arasında bire bir eşleme olmaz . Bir sabit Nseçilirse, bazen belirli doubledeğerler için gerçekten gerekenden biraz daha fazla veya daha az olacaktır . Çok az ( a)aşağıda) veya çok fazla ( aşağıda) hata verebiliriz b).

3 aday N:

a) Metin-metinden Ndönüştürürken doubleherkes için aynı metne ulaştığımız şekilde bunu kullanın double.

std::cout << dbl::digits10 << '\n';
// Typical output
15

b) bir kullanma Ndönüştürürken çok double-text- doubleaynı varmak doubleiçin tüm double.

// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17

Ne zaman max_digits10mevcut değildir nedeniyle tabanın 2 ve baz 10 özelliklerine, o notu digits10 + 2 <= max_digits10 <= digits10 + 3, kullanabileceğimiz digits10 + 3yeterli ondalık basamak sigorta yazdırılır.

c) Değere Ngöre değişen bir kullanın .

Bu, kod en az metni ( N == 1) veya tam bir değeri double( N == 1000-ishdurumunda denorm_min). Ancak bu “iş” olduğu ve muhtemelen OP'nin hedefi olmadığı için bir kenara bırakılacaktır.


Genellikle b) " doubledeğeri tam hassasiyetle basmak" için kullanılır . Bazı uygulamalar a) çok fazla bilgi vermemede hata yapmayı tercih edebilir.

İle .scientific, .precision()ondalık noktadan sonra yazdırılacak basamak sayısını ayarlar, böylece 1 + .precision()basamaklar yazdırılır. Kodun max_digits10toplam basamağa ihtiyacı var,.precision() , ile çağrılır max_digits10 - 1.

typedef std::numeric_limits< double > dbl;
std::cout.precision(dbl::max_digits10 - 1);
std::cout << std::scientific <<  exp (-100) << '\n';
std::cout << std::scientific <<  exp (+100) << '\n';
// Typical output
3.7200759760208361e-44
2.6881171418161356e+43
//1234567890123456  17 total digits

Benzer C sorusu


Mükemmel cevap! Yine de birkaç açıklama: precision()Bilimsel mod için ondalık basamak sayısını belirleyen haklısınız . Belirtmeden scientific, üs hariç toplam basamak sayısını ayarlar. Sayı değerinize bağlı olarak yine de bilimsel çıktı alabilirsiniz, ancak daha sonra belirttiğinizden daha az hane alabilirsiniz. Örnek: için cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"sonuçlar printffarklı olabilir. Kafa karıştırıcı şeyler farkında olmalıdır.
Simpleton

Posterity için, burada printf kullanarak tüm çift sayıların bilimsel modda garantili tam dize temsili için gereken arabellek uzunluğu: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);Ekstra karakterler şöyledir: işaret, ondalık nokta, arka sıfır, e [+ | -], üs için 3 basamak ( DBL_MAX_10_EXP = 308). Bu nedenle gerekli toplam karakter sayısı 25'tir.
Simpleton

İlk yorumumu düzenleyemiyorum, işte tekrar başlıyoruz: Bilimsel modla ilgili başka bir sorun, üstel çıktı kullanmamaya karar verebilmesi, hatta kayan nokta çıktısı kullanmamaya karar verebilmesidir. Yani, seriyi / serileştirme bağlamında bir sorun olabilecek 1.0 değerini "1" olarak verir. "% #. * G" kullanarak ondalık bir noktaya çıkmaya zorlayabilirsiniz, ancak bu # olmayan bir dizi sondaki sıfırlar eklediği dezavantajı vardır ...
Simpleton

3
printf("%.12f", M_PI);

% .12f, 12 basamak hassasiyetle kayan nokta anlamına gelir.


11
Bu "cout kullanmak" değildir.
Johnsyweb

2
12 basamak "tam hassasiyet" değildir
Roland Illig

0

En portatif olarak ...

#include <limits>

using std::numeric_limits;

    ...
    cout.precision(numeric_limits<double>::digits10 + 1);
    cout << d;

16
Merak ediyorum: neden "+1"?
Éric Malenfant

0

Ostream ile :: duyarlık (int)

cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;

getirecek

3.141592653589793, 2.718281828459045

Neden "+1" demek zorundasınız? Hiçbir fikrim yok, ama bundan elde ettiğiniz ekstra rakam doğrudur.


3
numeric_limits <unsigned char> :: rakam 10, 2'ye eşittir. Çünkü ondalık sayıdan herhangi iki ondalık sayı 0,99 içerebilir. Ayrıca 255 içerebilir ama 256, 257 ... 300 vb. İçermez. Bu nedenle basamak 10 3 değildir! Bunun gibi bir şeyin üstesinden gelmek için "+1" eklendiğini düşünüyorum.
Dmitriy Yurchenko

0

Bu noktadan sonra iki ondalık basamağa kadar olan değeri gösterecektir.

#include <iostream>
#include <iomanip>

double d = 2.0;
int n = 2;
cout << fixed << setprecison(n) << d;

Buraya bakın: Sabit noktalı gösterim

std :: sabit

Sabit kayan nokta gösterimini kullan Str akışı için kayan nokta biçimi bayrağını sabit olarak ayarlar.

Kayan alan sabit olarak ayarlandığında, kayan nokta değerleri sabit nokta gösterimi kullanılarak yazılır: değer, tam olarak ondalık kısımda, kesinlik alanı (kesinlik) tarafından belirtilen sayıda basamak ile temsil edilir ve üs parçası yoktur.

std :: setprecision

Ondalık duyarlığı ayarlama Çıktı işlemlerinde kayan nokta değerlerini biçimlendirmek için kullanılacak ondalık duyarlığı ayarlar.

Kayan noktaları temsil etmek için IEEE standardını biliyorsanız, standartın kapsamı dışında tam hassasiyetle kayan noktaları göstermenin imkansız olduğunu bilirsiniz , yani her zaman gerçek değerin yuvarlanması.

İlk önce değerin kapsam dahilinde olup olmadığını kontrol etmeniz gerekir , eğer evetse, şunu kullanın:

cout << defaultfloat << d ;

std :: defaultfloat

Varsayılan kayan nokta gösterimini kullan str akışı için kayan nokta biçimi bayrağını defaultfloat olarak ayarlar.

Kayan alan varsayılan yüzeye ayarlandığında, kayan nokta değerleri varsayılan gösterim kullanılarak yazılır: gösterim, akışın ondalık duyarlığına (kesinlik) kadar gerektiği kadar çok sayıda anlamlı basamak kullanır ve ondalık noktadan önceki ve sonraki sayıları sayar (varsa) ).

Bu aynı zamanda varsayılan davranışıdır cout, yani açıkça kullanmazsınız.

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.