Niedawno napotkałem problem, który można łatwo rozwiązać za pomocą dzielenia modułu, ale dane wejściowe były zmiennoprzecinkowe:
Biorąc pod uwagę funkcję okresową (np.
sin
) I funkcję komputera, która może ją obliczyć tylko w zakresie okresu (np. [-Π, π]), utwórz funkcję, która może obsłużyć dowolne dane wejściowe.
„Oczywistym” rozwiązaniem jest coś takiego:
#include <cmath>
float sin(float x){
return limited_sin((x + M_PI) % (2 *M_PI) - M_PI);
}
Dlaczego to nie działa? Otrzymuję ten błąd:
error: invalid operands of types double and double to binary operator %
Co ciekawe, działa w Pythonie:
def sin(x):
return limited_sin((x + math.pi) % (2 * math.pi) - math.pi)
sin(x)
dla dużych wartościx
faktycznie wymaga bardzo trudnego transcendentalnego procesu redukcji argumentów, którego nie da się obejść z żadnym skończonym przybliżeniem liczby pi.fmod
jest to prawdopodobnie to, czego szuka instruktor.Odpowiedzi:
Ponieważ zwykłe matematyczne pojęcie „reszty” ma zastosowanie tylko do dzielenia liczb całkowitych. tj. dzielenie wymagane do wygenerowania ilorazu całkowitego.
Aby rozszerzyć pojęcie „reszty” na liczby rzeczywiste, należy wprowadzić nowy rodzaj operacji „hybrydowej”, która generowałaby iloraz liczb całkowitych dla rzeczywistych operandów. Język Core C nie obsługuje takiej operacji, ale jest dostarczany jako standardowa
fmod
funkcja biblioteczna , a takżeremainder
funkcja w C99. (Zauważ, że te funkcje nie są takie same i mają pewne cechy szczególne. W szczególności nie są zgodne z zasadami zaokrąglania dzielenia liczb całkowitych).źródło
(a/b)*b
już jest równaa
[o ile taka instrukcja może być wykonana dla typów zmiennoprzecinkowych], więca%b
nigdy nie byłaby szczególnie przydatna.%
operatora dla zmiennoprzecinkowego, jak przypuszczam, byłaby równaa-(a/b)*b
0 lub bardzo mała wartość.Szukasz fmod () .
Myślę, że aby dokładniej odpowiedzieć na twoje pytanie, w starszych językach
%
operator został po prostu zdefiniowany jako całkowity podział modularny, aw nowszych językach zdecydowano się rozszerzyć definicję operatora.EDYCJA: Gdybym miał się założyć, dlaczego, powiedziałbym, że to dlatego, że idea arytmetyki modularnej wywodzi się z teorii liczb i zajmuje się konkretnie liczbami całkowitymi.
źródło
Nie mogę powiedzieć na pewno , ale myślę, że to głównie historia. Sporo wczesnych kompilatorów C w ogóle nie obsługiwało zmiennoprzecinkowych. Został dodany później, a nawet wtedy nie tak kompletnie - dodano głównie typ danych i najbardziej prymitywne operacje obsługiwane w języku, ale wszystko inne pozostawiono w bibliotece standardowej.
źródło
Operator modulo
%
w C i C ++ jest zdefiniowany dla dwóch liczb całkowitych, jednak istniejefmod()
funkcja dostępna do użycia z podwójnymi.źródło
sin(fmod(x,3.14))
lub nawetsin(fmod(x,M_PI))
nie jest równysin(x)
dla dużych wartościx
. W rzeczywistości wartości mogą różnić się nawet o 2,0.%
jest operatorem reszty i nie jest operatorem modulo?Ograniczenia znajdują się w normach:
C11 (ISO / IEC 9899: 201x) §6.5.5 Operatory multiplikatywne
C ++ 11 (ISO / IEC 14882: 2011) §5.6 Operatory multiplikatywne
Rozwiązaniem jest użycie
fmod
, i właśnie dlatego operandy%
są w pierwszej kolejności ograniczone do typu całkowitego, zgodnie z uzasadnieniem C99 §6.5.5 Operatory multiplikatywne :źródło
próbować
fmod
źródło
Operator% podaje REMAINDER (inną nazwę modułu) liczby. W przypadku C / C ++ jest to zdefiniowane tylko dla operacji na liczbach całkowitych. Python jest nieco szerszy i pozwala uzyskać pozostałą część liczby zmiennoprzecinkowej dla pozostałej części tego, ile razy można ją podzielić:
>>> 4 % math.pi 0.85840734641020688 >>> 4 - math.pi 0.85840734641020688 >>>
źródło
:P
%
Operator nie działa w C ++, kiedy próbują znaleźć resztę z dwóch liczb, które są zarówno typuFloat
lubDouble
.Dlatego możesz spróbować użyć
fmod
funkcji zmath.h
/cmath.h
lub możesz użyć tych linii kodu, aby uniknąć używania tego pliku nagłówkowego:float sin(float x) { float temp; temp = (x + M_PI) / ((2 *M_PI) - M_PI); return limited_sin((x + M_PI) - ((2 *M_PI) - M_PI) * temp ));
}
źródło
„Matematyczne pojęcie arytmetyki modulo sprawdza się również w przypadku wartości zmiennoprzecinkowych i jest to jedna z pierwszych kwestii, które Donald Knuth omawia w swojej klasycznej sztuce programowania komputerowego (tom I). To znaczy była to kiedyś podstawowa wiedza”.
Operator modułu zmiennoprzecinkowego jest zdefiniowany w następujący sposób:
m = num - iquot*den ; where iquot = int( num/den )
Jak wskazano, brak operacji operatora% na liczbach zmiennoprzecinkowych wydaje się być związany ze standardami. CRTL zapewnia „fmod”, a zwykle także „resztę”, aby wykonać% na liczbach fp. Różnica między tymi dwoma polega na tym, jak radzą sobie z pośrednim zaokrągleniem „iquot”.
„reszta” używa zaokrąglenia do najbliższej, a „fmod” używa prostego obcięcia.
Jeśli piszesz własne klasy numeryczne w C ++, nic nie stoi na przeszkodzie, aby zmienić starszą wersję bez operacji, włączając przeciążony operator%.
Z poważaniem
źródło