Liczby całkowite
Generalnie nie chcemy używać podwójnych, ponieważ nie chcemy używać operacji zmiennoprzecinkowych, błędów zaokrągleń itp. Po prostu nie są one potrzebne.
W tym celu warto pamiętać, jak wykonać podział sufitu: ceil(x / y)
w podwójnych można zapisać jako (x + y - 1) / y
(unikając liczb ujemnych, ale uważaj na przepełnienie).
Czytelny
Jeśli zależy Ci na czytelności, możesz oczywiście zaprogramować ją w ten sposób (na przykład w Javie, dla C możesz oczywiście użyć makr):
public static int ceilDiv(int x, int y) {
return (x + y - 1) / y;
}
public static int paddedBase64(int n) {
int blocks = ceilDiv(n, 3);
return blocks * 4;
}
public static int unpaddedBase64(int n) {
int bits = 8 * n;
return ceilDiv(bits, 6);
}
// test only
public static void main(String[] args) {
for (int n = 0; n < 21; n++) {
System.out.println("Base 64 padded: " + paddedBase64(n));
System.out.println("Base 64 unpadded: " + unpaddedBase64(n));
}
}
Podszewka
Watowany
Wiemy, że potrzebujemy jednocześnie 4 bloków znaków na każde 3 bajty (lub mniej). Zatem wzór wygląda następująco (dla x = n i y = 3):
blocks = (bytes + 3 - 1) / 3
chars = blocks * 4
lub połączone:
chars = ((bytes + 3 - 1) / 3) * 4
Twój kompilator zoptymalizuje plik 3 - 1
, więc zostaw to tak, aby zachować czytelność.
Miękki
Mniej powszechny jest wariant bez wypełnienia, w tym celu pamiętamy, że każdy potrzebujemy znaku na każde 6 bitów, zaokrąglone w górę:
bits = bytes * 8
chars = (bits + 6 - 1) / 6
lub połączone:
chars = (bytes * 8 + 6 - 1) / 6
możemy jednak jeszcze podzielić przez dwa (jeśli chcemy):
chars = (bytes * 4 + 3 - 1) / 3
Nieczytelne
W przypadku, gdy nie ufasz swojemu kompilatorowi, który wykona za Ciebie ostateczne optymalizacje (lub jeśli chcesz zmylić kolegów):
Watowany
((n + 2) / 3) << 2
Miękki
((n << 2) | 2) / 3
Mamy więc dwa logiczne sposoby obliczania i nie potrzebujemy żadnych gałęzi, operacji bit-op lub modulo - chyba, że naprawdę tego chcemy.
Uwagi:
- Oczywiście może być konieczne dodanie 1 do obliczeń, aby uwzględnić zerowy bajt końcowy.
- W przypadku Mime może być konieczne zajęcie się możliwymi znakami zakończenia linii i tym podobnymi (poszukaj innych odpowiedzi na to).