Bir kayan değeri (37.777779 gibi) C'deki iki ondalık basamağa (37.78) nasıl yuvarlayabilirim?
Bir kayan değeri (37.777779 gibi) C'deki iki ondalık basamağa (37.78) nasıl yuvarlayabilirim?
Yanıtlar:
Çıktı amacıyla sayıyı yuvarlamak istiyorsanız, "%.2f"
biçim dizesi gerçekten doğru cevaptır. Ancak, daha fazla hesaplama için kayan nokta değerini yuvarlamak istiyorsanız, aşağıdakine benzer:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
Seçmek isteyebileceğiniz üç farklı yuvarlama kuralı olduğuna dikkat edin: aşağı yuvarlayın (yani, iki ondalık basamaktan sonra kısaltın), en yakına yuvarlayın ve yuvarlayın. Genellikle, en yakına yuvarlamak istersiniz.
Bazılarının işaret ettiği gibi, kayan nokta gösterimi tuhaflıkları nedeniyle, bu yuvarlanmış değerler tam olarak "açık" ondalık değerler olmayabilir, ancak çok çok yakın olacaktır.
Yuvarlama hakkında çok daha fazla bilgi için ve özellikle en yakınına yuvarlama için bağlayıcı kurallar hakkında daha fazla bilgi için Yuvarlama hakkındaki Wikipedia makalesine bakın .
doubles
şekilde yapabilir miyim ? İstediğim işi yapmıyormuş gibi görünüyor :( ( floor
ve kullanarak ceil
).
Printf içinde % .2f kullanma . Yalnızca 2 ondalık nokta yazdırır.
Misal:
printf("%.2f", 37.777779);
Çıktı:
37.77
float
gibi aralık kaybı olmaması avantajına da sahiptir val * 100
.
Eğer baskı için değer yuvarlak bahsediyoruz varsayarsak, o zaman Andrew Coleson ve Arak 'ın cevabı doğrudur:
printf("%.2f", 37.777779);
Ancak, dahili kullanım için sayıyı tam olarak 37,78'e yuvarlamayı hedefliyorsanız (örneğin, başka bir değerle karşılaştırmak için), kayan nokta sayılarının çalışma şekli nedeniyle bunun iyi bir fikir olmadığını unutmayın: genellikle kayan nokta için eşitlik karşılaştırmaları yapmak yerine, bir hedef değeri +/- bir sigma değeri kullanın. Ya da sayıyı bilinen bir kesinlik ile dize olarak kodlayın ve karşılaştırın.
Greg Hewgill'in finansal hesaplamalar için neden kayan nokta kullanmamanız gerektiğini de kapsayan ilgili bir soruya verdiği cevaba bakın .
printf("%.*f", (int)precision, (double)number);
Buna ne dersin:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
C-string'e yazmak istiyorsanız:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
A'yı float
diğerine yuvarlamanın bir yolu yoktur float
çünkü yuvarlatılmış float
temsil edilemeyebilir (kayan noktalı sayıların bir sınırlaması). Örneğin, 37.777779'u 37.78'e yuvarladığınızı, ancak temsil edilebilecek en yakın sayı 37.781 olduğunu varsayalım.
Ancak, can "yuvarlak" bir float
bir biçim dizesi işlevini kullanarak.
float
ile n arasındaki ondalık basamakları yuvarlayamazsınız ve sonuçta her zaman n ondalık basamak olmasını beklemezsiniz. Hala float
beklediğin değil, bir alacaksın.
Ayrıca, C ++ kullanıyorsanız, sadece böyle bir işlev oluşturabilirsiniz:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Ardından herhangi bir çift çıkış myDouble
ile n
bu gibi kodla ondalık noktadan sonraki basamağa:
std::cout << prd(myDouble,n);
Yine de şunları kullanabilirsiniz:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
misal:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
C ++ 'da (veya C stili dökümlerle C'de), işlevi oluşturabilirsiniz:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
Sonra std::cout << showDecimals(37.777779,2);
üretecek: 37.78.
Açıkçası bu işlevdeki 5 değişkenin tamamını oluşturmanıza gerek yok, ama mantığı görebilmek için onları orada bırakıyorum. Muhtemelen daha basit çözümler var, ama bu benim için iyi çalışıyor - özellikle ondalık basamaktan sonraki basamak sayısını ihtiyacım olarak ayarlamama izin verdiği için.
Bunun için her zaman printf
fonksiyon ailesini kullanın . Değeri bir kayan nokta olarak almak isteseniz bile, en iyi snprintf
yuvarlatılmış değeri bir dize olarak almak ve daha sonra aşağıdakileri kullanarak ayrıştırmak için kullanamazsınız atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
Bunu söylüyorum, çünkü şu anda en çok oy alan cevap ve burada bulunan diğer birkaç kişi tarafından gösterilen yaklaşım - 100 ile çarpılıyor, en yakın tam sayıya yuvarlanıyor ve sonra tekrar 100'e bölünüyor - iki şekilde kusurlu:
İlk hata türünü (yuvarlama yönü bazen yanlış) göstermek için bu programı çalıştırmayı deneyin:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Bu çıktıyı göreceksiniz:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Başladığımız değerin 0,015'ten az olduğunu ve bu nedenle 2 ondalık basamağa yuvarlarken matematiksel olarak doğru cevabın 0,01 olduğunu unutmayın. Tabii ki, 0.01 tam olarak bir çift olarak temsil edilemez , ancak sonucumuzun 0.01'e en yakın çift olmasını bekliyoruz. Kullanmak snprintf
bize bu sonucu verir, ancak kullanmak round(100 * x) / 100
bize 0.02 verir, bu yanlıştır. Neden? Çünkü 100 * x
sonuç olarak bize tam olarak 1.5 veriyor. 100 ile çarpıldığında, yuvarlamak için doğru yön değişir.
Örnek olarak ikinci arızanın cinsini - Sonuç bazen nedeniyle yanlış olma * 100
ve / 100
gerçekten birbirlerine tersidir olmamak - biz çok büyük bir sayı ile benzer bir egzersiz yapabilirsiniz:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Artık sayımızın kesirli bir parçası bile yok; yalnızca tür ile depolanan bir tamsayı değeridir double
. Yuvarlamadan sonraki sonuç, başladığımız sayı ile aynı olmalı, değil mi?
Yukarıdaki programı çalıştırırsanız şunları görürsünüz:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Hata. Yöntemimiz snprintf
tekrar doğru sonucu döndürür, ancak çarpma-sonra-yuvarlak-sonra-bölme yaklaşımı başarısız olur. Matematiksel doğru değeri olmasıdır 8631192423766613.0 * 100
, 863119242376661300.0
değil, bir çift tam olarak sunulabilen; en yakın değer 863119242376661248.0
. Bunu 100'e böldüğünüzde, 8631192423766612.0
başladığınızdan farklı bir sayı elde edersiniz.
Umarım bu roundf
, birkaç ondalık basamağa yuvarlamak için kullanmanın bozulduğunu ve bunun snprintf
yerine kullanmanız gerektiğini gösteren yeterli bir gösteri . Bu size korkunç bir saldırı gibi geliyorsa, belki de CPython'un yaptığı şeyden emin olabilirsiniz .
Kullanın float roundf(float x)
.
"Yuvarlama işlevleri, argümanlarını kayan nokta biçiminde en yakın tamsayı değerine yuvarlar ve mevcut yuvarlama yönüne bakılmaksızın, yarım vakaları sıfırdan uzağa yuvarlar." C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Uygulamanıza bağlı olarak, float
yarı yolda görünebilecek sayılar değildir. kayan nokta tipik olarak baz-2 yönelimlidir. Ayrıca, 0.01
tüm "yarı yol" davalarında en yakın olana tam olarak yuvarlamak en zordur.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
"1.115" Her ne kadar dönüştürülür 1.11 ve 1.12 arasında, "yarım" bir float
değer olup, 1.115000009537...
ve "yarım" artık, ama daha yakından 1.12 ve mermi için en yakın olan float
ve1.120000004768...
"1.125", 1.12 ve 1.13 arasında "yarı yol" dur, dönüştürüldüğünde float
değer tam olarak 1.125
ve "yarı yol" dur. Dolayı en yakın bile kurala bağları ve mermi ile 1.13 doğru yuvarlar float
arasında1.129999995232...
"1.135" Her ne kadar dönüştürülür 1.13 ve 1.14 arasında, "yarım" bir float
değer olup, 1.134999990463...
ve "yarım" artık, ama daha yakından 1.13 ve mermi için en yakın olan float
ve1.129999995232...
Kod kullanılırsa
y = roundf(x*100.0f)/100.0f;
"1.135" dönüştürülür 1.13 ve 1.14 arasında "yarım", her ne kadar float
, bir değerdir 1.134999990463...
ve "yarım" artık, ama daha yakından 1.13 ama yanlış için mermi float
arasında 1.139999985695...
daha sınırlı hassas nedeniyle float
vs double
. Bu yanlış değer, kodlama hedeflerine bağlı olarak doğru olarak görülebilir.
Bu makroyu float sayılarını yuvarlamak için yaptım. Başlığınıza / dosya varlığınıza ekleyin
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
İşte bir örnek:
float x = ROUNDF(3.141592, 100)
x eşittir 3.14 :)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
İşte n
ondalık sayıların sayısı
misal:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
büyük olasılıkla çökecektir 3) her dalda aynı şeyi yaptığınız garip if
/ else
blok ve 4) sprintf
ikinci bir sprintf
çağrı için format belirleyiciyi oluşturmak için aşırı karmaşık kullanımı ; .*
aynı sprintf
çağrının argüman olarak çift değerini ve ondalık basamak sayısını kullanmanız ve iletmeniz daha kolaydır .
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
Öncelikle bu soruya başka bir cevap daha eklememe nedenimi gerekçelendirmeye çalışayım. İdeal bir dünyada, yuvarlama gerçekten önemli değildir. Bununla birlikte, gerçek sistemlerde, beklediğiniz gibi olmayan yuvarlama ile sonuçlanabilecek çeşitli sorunlarla uğraşmanız gerekebilir. Örneğin, nihai sonuçların yuvarlandığı ve kullanıcılara 2 ondalık basamak olarak gösterildiği finansal hesaplamalar yapıyor olabilirsiniz; bu aynı değerler 2'den fazla ondalık basamak içerebilecek bir veritabanında sabit bir hassasiyetle depolanır (çeşitli nedenlerle saklanacak en uygun yer sayısı yoktur ... her sistemin desteklemesi gereken belirli durumlara bağlıdır, örneğin fiyatları birim başına bir kuruşun kısımlarıdır); ve sonuçların artı / eksi epsilon olduğu değerlerde gerçekleştirilen kayan nokta hesaplamaları. Yıllardır bu sorunlarla yüzleşiyorum ve kendi stratejimi geliştiriyorum. Her senaryo ile karşılaştığımı veya en iyi cevaba sahip olduğumu iddia etmeyeceğim, ancak aşağıda bu konuların üstesinden gelen yaklaşımımın bir örneği:
Aşağıdaki yuvarlama işlevini / yöntemini kullanarak şamandıralar / çiftler (belirli bir uygulama için keyfi bir karar) üzerinde hesaplamalar için 6 ondalık basamağın yeterli hassasiyet olarak kabul edildiğini varsayalım:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
Bir sonucun sunumu için 2 ondalık basamağa yuvarlama şu şekilde yapılabilir:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Çünkü val = 6.825
sonuç 6.83
beklendiği gibi.
Çünkü val = 6.824999
sonuç 6.82
. Burada varsayım, hesaplamanın tam olarak sonuçlandığı 6.824999
ve 7. ondalık basamağın sıfır olduğu varsayımıdır .
Çünkü val = 6.8249999
sonuç 6.83
. 9
Bu durumda 7. ondalık basamak , Round(val,6)
işlevin beklenen sonucu vermesine neden olur . Bu durumda, herhangi bir sayıda takip edilebilir 9
.
Çünkü val = 6.824999499999
sonuç 6.83
. Birinci basamak olarak 8. ondalık basamağa yuvarlama, yani Round(val,8)
, hesaplanan bir kayan nokta sonucunun hesaplandığı 6.8249995
, ancak dahili olarak temsil edildiği tek kötü durumla ilgilenir 6.824999499999...
.
Son olarak, sorudan örnek ... ile val = 37.777779
sonuçlanır 37.78
.
Bu yaklaşım şu şekilde genelleştirilebilir:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
burada N, şamandıralar / çiftler üzerindeki tüm ara hesaplamalar için korunacak hassasiyettir. Bu negatif değerler üzerinde de çalışır. Bu yaklaşımın tüm olasılıklar için matematiksel olarak doğru olup olmadığını bilmiyorum.
Bir sayıyı yuvarlamak için basit C kodu:
float n = 3.56;
printf("%.f", n);
Bu Çıktı olacaktır:
4
... ya da herhangi bir kütüphane olmadan eski moda bir şekilde yapabilirsiniz:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Tabii ki numaradan ekstra bilgileri kaldırmak istiyorsanız.
bu işlev sayı ve hassasiyeti alır ve yuvarlanmış sayıyı döndürür
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
noktayı sola kaydırarak ve beşten fazla durumu kontrol ederek kayan nokta sayısını int'e dönüştürür.
float
(vedouble
) ondalık kayan nokta değildir - ikili kayan noktadır - bu nedenle ondalık konumlara yuvarlama anlamsızdır. Ancak çıktıyı yuvarlayabilirsiniz.