2012-06-26 13 views
16

chcę zaokrąglić liczbę typu float do danego precyzją, na przykład:Okrągły pływaka do danego precyzją

0.051 i want to convert it to 
0.1 

0.049 i want to convert it to 
0.0 

0.56 i want to convert it to 
0.6 

0.54 i want to convert it to 
0.5 

ja mogę to wytłumaczyć lepiej, ale powodem tego jest przełożenie lokalizację punktu (jak 0.131f, 0.432f) do położenia płytki w siatce (jak 0.1f, 0.4f).

Czy jest jakiś sposób, aby to osiągnąć?

+0

możliwe duplikat (http://stackoverflow.com/questions/5834368/how -to-round-to-integer-natural-in-c) –

+0

Zaokrąglanie liczby zmiennoprzecinkowej do 1 lub więcej miejsc dziesiętnych nie ma większego sensu; liczba taka jak "0,1" nie może być dokładnie odwzorowana w binarnym zmiennoprzecinkowaniu. Zaokrąglanie można wykonać na wyjściu (do napisu lub do pliku). –

+0

Jesteśmy w dziale IT, staramy się reprezentować nieskończone możliwości realnego świata w liniach kodu, wszystko ma sens tutaj. Używam tego do nieskończonego przewijania tła w grze, stracona precyzja nie ma znaczenia. – SteveL

Odpowiedz

21

Dopóki twoja siatka jest regularna, po prostu znajdź transformację z liczb całkowitych do tej siatki. Więc załóżmy, że siatka jest

0.2 0.4 0.6 ... 

potem runda

float round(float f) 
{ 
    return floor(f * 5 + 0.5)/5; 
    // return std::round(f * 5)/5; // C++11 
} 
+1

dziękuję, to jest proste i działa idealnie !!! – SteveL

+0

Wygląda na to, że nie działa poprawnie. 0,5 jest konwertowane na 0,6. – Niki

+1

@Niki ... i to jest to, czego można się spodziewać, prawda? – marton78

7

Standardowa ceil(), floor() funkcje nie mają dokładność, myślę, że mógłby obejść dodając swoją precyzję - ale to może wprowadzać błędy - na przykład

double ceil(double v, int p) 
{ 
    v *= pow(10, p); 
    v = ceil(v); 
    v /= pow(10, p); 
} 

Zgaduję, że możesz sprawdzić, czy jest to dla ciebie niezawodne?

+0

Czy nie jest to/= pow (10, p) LUB * = pow (10, -p)? –

+0

@ AurélienOoms, dzięki, mała literówka ... – Nim

3

Korzystanie floor() i ceil(). floor będzie przekonwertować pływaka do następnego mniejszej liczby całkowitej, a ceil do następnego wyższa:

floor(4.5); // returns 4.0 
ceil(4.5); // returns 5.0 

myślę następujących będzie działać:

float round(float f) 
{ 
    return floor((f * 10) + 0.5)/10; 
} 

floor(f + 0.5) będzie zaokrąglić do liczby całkowitej. Po pierwszym pomnożeniu przez 10, a następnie podzieleniu wyniku przez 10, zaokrąglasz przyrosty o 0,1.

+0

Co powiesz na ujemne pływaki? (Używałem tego dla dodatnich wartości pływalności przed lepszym pokryciem std lib.) – sage

5

Algorytm można użyć:

  • uzyskać 10-do-moc (liczba-znaczącej cyfr) (= P10)
  • pomnożyć swoje podwójnej wartości przez P10
  • dodać: 0.5 (lub odjąć, jeśli ujemne - patrz komentarz Ankush szacha)
  • podzielić całkowitą, część tej sumy przez (P10) - odpowiedź będzie numer zaokrąglone
+2

mały punkt: jeśli liczba jest ujemna, będziesz musiał odjąć 0,5 –

5

ED IT 1: Szukałem rozwiązań dla numpy w python i nie zdawałem sobie sprawy, że OP poprosił o C++ haha, no cóż.

EDYCJA 2: Lol, wygląda na to, że nawet nie odpowiedziałem na twoje pierwotne pytanie. Wygląda na to, że naprawdę chcesz zaokrąglić w kolejności dziesiętnej (operacja jest niezależna od podanej liczby), a nie precyzji (operacja zależy od liczby), a inni już to rozwiązali.

Właściwie to rozglądałem się za tym, ale nie mogłem znaleźć czegoś, więc zrzuciłem implementację dla numpy array. Wygląda na to, że implementuje logikę, o której mówi slashmais.

def pround(x, precision = 5): 
    temp = array(x) 
    ignore = (temp == 0.) 
    use = logical_not(ignore) 
    ex = floor(log10(abs(temp[use]))) - precision + 1 
    div = 10**ex 
    temp[use] = floor(temp[use]/div + 0.5) * div 
    return temp 

Oto C++ wersja skalarne, jak również i prawdopodobnie można by było zrobić coś podobnego do wyżej, stosując Eigen (mają logiczne indeksowanie): (I wziął również to jako szansę ćwiczyć trochę więcej impuls haha):

#include <cmath> 
#include <iostream> 
#include <vector> 
#include <boost/foreach.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 

using namespace std; 

double pround(double x, int precision) 
{ 
    if (x == 0.) 
     return x; 
    int ex = floor(log10(abs(x))) - precision + 1; 
    double div = pow(10, ex); 
    return floor(x/div + 0.5) * div; 
} 

    template<typename T> 
vector<T>& operator<<(vector<T> &x, const T &item) 
{ 
    x.push_back(item); 
    return x; 
} 

int main() 
{ 
    vector<double> list; 
    list << 0.051 << 0.049 << 0.56 << 0.54; 
    // What the OP was wanting 
    BOOST_FOREACH(double x, list) 
    { 
     cout << floor(x * 10 + 0.5)/10 << "\n"; 
    } 

    cout << "\n"; 

    BOOST_FOREACH(double x, list) 
    { 
     cout << pround(x, 0) << "\n"; 
    } 

    cout << "\n"; 

    boost::function<double(double)> rounder = boost::bind(&pround, _1, 3); 
    vector<double> newList; 
    newList << 1.2345 << 1034324.23 << 0.0092320985; 
    BOOST_FOREACH(double x, newList) 
    { 
     cout << rounder(x) << "\n"; 
    } 

    return 0; 
} 

wyjściowa:

0.1 
0 
0.6 
0.5 

0.1 
0 
1 
1 

1.23 
1.03e+06 
0.00923 
0

Od mooing Duck edytowany moje pytanie i usunął kod mówiąc pytania nie powinny zawierać odpowiedzi (zrozumiałe), będę pisać rozwiązanie tutaj:

float round(float f,float prec) 
{ 
    return (float) (floor(f*(1.0f/prec) + 0.5)/(1.0f/prec)); 
} 
2

Zazwyczaj znana jest wymagana precyzja podczas kompilacji. Dlatego też, przy użyciu matrycy funkcji Pow dostępny here można zrobić:

template <int PRECISION> 
float roundP(float f) 
{ 
    const int temp = Pow<10,PRECISION>::result; 
    return roundf(f*temp)/temp; 
} 

int main() { 
    std::cout << std::setprecision(10); 
    std::cout << roundP<0>(M_PI) << std::endl; 
    std::cout << roundP<1>(M_PI) << std::endl; 
    std::cout << roundP<2>(M_PI) << std::endl; 
    std::cout << roundP<3>(M_PI) << std::endl; 
    std::cout << roundP<4>(M_PI) << std::endl; 
    std::cout << roundP<5>(M_PI) << std::endl; 
    std::cout << roundP<6>(M_PI) << std::endl; 
    std::cout << roundP<7>(M_PI) << std::endl; 
} 

Testowany here.

Wynik pokazuje również, w jaki sposób nieprecyzyjny jest zmiennoprzecinkowych reprezentacji :)

3,099999905

3,140000105

3,14199996

3,141599894

3,141590118

3,141592979

3,141592741

Możesz mieć lepsze wyniki stosując podwójne:

template <int PRECISION> 
double roundP(double f) 
{ 
    const int temp = Pow<10,PRECISION>::result; 
    return round(f*temp)/temp; 
} 

Wydrukowano z dokładnością 20:

3,1000000000000000888

3,1400000000000001243

3,1419999999999999041

3,1415999999999999481

3,1415899999999998826

3,1415929999999998579

3.1415926999999999047

0

algorytm zaokrąglania liczby float:

double Rounding(double src, int precision) { 
     int_64 des; 
     double tmp; 
     double result; 
     tmp = src * pow(10, precision); 
     if(tmp < 0) {//negative double 
      des = (int_64)(tmp - 0.5); 
     }else { 
      des = (int_64)(tmp + 0.5); 
     } 
     result = (double)((double)dst * pow(10, -precision)); 
     return result; 
    } 
1

pokrótce optymalizacji na przestrzeni ostatnich kilku odpowiedzi, przekształcając numer wejścia do podwójnego pierwszy aby zapobiec przepełnieniu. Funkcja próbki (nie jest zbyt ładna, ale działa dobrze): [? Jak zaokrąglić do liczby całkowitej (naturalnie) w C++]

#include <cmath> 

// round float to n decimals precision 
float round_n (float num, int dec) 
{ 
    double m = (num < 0.0) ? -1.0 : 1.0; // check if input is negative 
    double pwr = pow(10, dec); 
    return float(floor((double)num * m * pwr + 0.5)/pwr) * m; 
}