2017-05-22 22 views
6

Potrzebuję zaokrąglić liczby całkowite, aby być najbliższą wielokrotnością innej liczby całkowitej. Przykłady wyników w przypadku wielokrotności 100:Zaokrąglanie liczby całkowitej do najbliższej wielokrotności innej liczby całkowitej

  • 36-> 0
  • 99-> 100
  • 123-> 100
  • 164-> 200

itd na.

wpadłem na następujący kod, który działa, ale czuje się „brudne”:

int RoundToMultiple(int toRound, int multiple) 
{ 
    return (toRound + (multiple/2))/multiple * multiple; 
} 

ten liczy na właściwości obcinania podziału całkowitej, aby to działało. Czy mogę liczyć na to, że ten kod będzie przenośny? Czy są jakieś konfiguracje kompilatora, w których to nie przyniesie pożądanego rezultatu? Jeśli tak, jak mogę osiągnąć te same wyniki w przenośny sposób?

W razie potrzeby, aby uzyskać lepszą odpowiedź, można założyć, że wielokrotności będą potęgami 10 (w tym wielokrotnościami 1). Można również założyć, że liczby są dodatnie.

+0

Prawdopodobnie nie będzie działać dla liczb ujemnych. – Gerriet

+0

@ Gerriet Dobry punkt, choć w moim przypadku liczby można również założyć jako pozytywne. Dodaję go do pytania: –

+0

Jak zachowa się RoundToMultiple (150, 100)? Czy powinien powrócić 100 czy 200? Innymi słowy, zaokrąglić w górę, zaokrąglić w dół lub użyć reguł zaokrąglania bankiera? –

Odpowiedz

4

Tak, możesz liczyć na to, że ten kod będzie przenośny. N4296 (jest to ostatni otwarty projekt C++, 14) mówi w rozdziale 5.6 [expr.mul]

Na integralnych operandów/operator uzyskuje algebraiczną iloraz z dowolną częścią ułamkową odrzucono. [Przypis: często nazywa się to obcięciem w kierunku zera]

To nie jest nowa funkcja najnowszego C++, na której można polegać również w C89.

Jedynym zastrzeżeniem jest to, że jeśli toRound jest negatywny, należy odjąć przesunięcie.

Alternatywnym podejściem jest:

int RoundToMultiple(int toRound, int multiple) 
{ 
    const auto ratio = static_cast<double>(toRound)/multiple; 
    const auto iratio = std::lround(ratio); 
    return iratio * multiple; 
} 

to uniknąć bałagan +/- przesunięcia, ale wydajność będzie gorzej, a jeśli istnieją problemy toRound jest tak duża, że ​​nie można dokładnie odbędzie się w podwójnej . (OTOH, jeśli to jest wyjście, to podejrzewam, że multiple będzie podobnie duży w tym przypadku, więc wszystko będzie w porządku.)

+0

['liczba int 'nie może przekroczyć dokładności włączonej przez' podwójne'] (https://stackoverflow.com/questions/3793838/which- is-the-first-integer-that-aneee-754-float-is-incapable-of-represent-e), więc prawdopodobnie nie potrzebujesz tego ostrzeżenia o tym, że 'toRound' staje się zbyt duży. – Xirema

+0

@Xirema przepraszam, ale gdzie w tym linku, który wysłałeś, jest napisane, że int precyzja nie może przekraczać podwójnej precyzji? –

+0

@KlitosKyriacou [pierwsza odpowiedź na ten post] (https://stackoverflow.com/a/3793950/5241642). Liczba podana dla 'double' jest znacznie większa niż maksymalny zakres' int'. – Xirema

2

++ średnia C wyraźnie wskazuje zachowanie podziału całkowitej wygląda następująco:

[expr.mul]

integralnego operandów przełącznika/operatora Plony algebraicznej iloraz z dowolną częścią ułamkową odrzucono.

A.k.a. obcięcie w kierunku zera. To jest tak przenośne, jak to tylko możliwe.

0

Chociaż - jak wspomniano przez innych - integralny podział zachowuje się zgodnie z oczekiwaniami, może być poniższe rozwiązanie wygląda "mniej przewodowo" (nadal oparte na opiniach).

Dotyczące rozwiązania, które przekształca int w podwójne: osobiście uważam, że jest to kosztowne tylko ze względu na zaokrąglenia, ale może ktoś może mnie przekonać, że moje odczucia są błędne;

W każdym razie, za pomocą operatorów tylko integralne, następujące rozwiązanie sprawia, że ​​dyskusja na temat tego, czy double „s mantysa zawsze może pomieścić każdą int zbędny:

int RoundToMultiple(int toRound, int multiple) { 
    toRound += multiple/2; 
    return toRound - (toRound%multiple); 
} 

Jeśli również chciał to wartości ujemne, kod można nieznacznie dostosować w następujący sposób (w tym testy):

#include <stdio.h> 

int RoundToMultiple(int toRound, int multiple) { 
    toRound += toRound < 0 ? -multiple/2 : multiple/2; 
    return toRound - (toRound%multiple); 
} 

int main(int argc, char const *argv[]) 
{ 
    int tests[] = { 36,99,123,164,-36,-99,-123,-164,0 }; 
    int expectedResults[] = { 0,100,100,200,0,-100,-100,-200,0 }; 

    int i=0; 
    int test=0, result=0, expectedResult=0; 
    do { 
     test = tests[i]; 
     result = RoundToMultiple(test, 100); 
     expectedResult = expectedResults[i]; 
     printf("test %d: %d==%d ? %s\n", test, result, expectedResult, (expectedResult==result ? "OK" : "NOK!")); 
     i++; 
    } 
    while(test != 0); 
} 
Powiązane problemy