Недавно я столкнулся с проблемой, которую можно было легко решить с помощью модульного деления, но ввод был с плавающей точкой:
Учитывая периодическую функцию (например,
sin
) и компьютерную функцию, которая может вычислять ее только в пределах диапазона периодов (например, [-π, π]), создайте функцию, которая может обрабатывать любой ввод.
«Очевидное» решение выглядит примерно так:
#include <cmath>
float sin(float x){
return limited_sin((x + M_PI) % (2 *M_PI) - M_PI);
}
Почему это не работает? Я получаю такую ошибку:
error: invalid operands of types double and double to binary operator %
Интересно, что в Python это работает:
def sin(x):
return limited_sin((x + math.pi) % (2 * math.pi) - math.pi)
sin(x)
для больших значенийx
фактически требует очень сложного трансцендентного процесса редукции аргумента, который не может обойтись никаким конечным приближением числа пи.fmod
это, вероятно, то, что ищет инструктор.Ответы:
Потому что обычное математическое понятие «остаток» применимо только к целочисленному делению. т.е. деление, необходимое для создания целочисленного частного.
Чтобы распространить понятие «остаток» на действительные числа, вы должны ввести новый вид «гибридной» операции, которая будет генерировать целочисленное частное для реальных операндов. Базовый язык C не поддерживает такую операцию, но он предоставляется как
fmod
функция стандартной библиотеки , а также какremainder
функция в C99. (Обратите внимание, что эти функции не совпадают и имеют некоторые особенности. В частности, они не следуют правилам округления целочисленного деления.)источник
(a/b)*b
уже равноa
[в той мере, в какой такой оператор может быть сделан для типов с плавающей запятой], поэтомуa%b
никогда не будет особенно полезен.%
Я полагаю, что наиболее подходящим определением оператора для плавающей точкиa-(a/b)*b
было бы либо 0, либо очень маленькое значение.Вы ищете fmod () .
Я предполагаю, чтобы более конкретно ответить на ваш вопрос, в старых языках
%
оператор был просто определен как целочисленное модульное деление, а в новых языках они решили расширить определение оператора.РЕДАКТИРОВАТЬ: Если бы я мог предположить, почему, я бы сказал, что это потому, что идея модульной арифметики берет начало в теории чисел и имеет дело конкретно с целыми числами.
источник
Я не могу сказать точно , но думаю, это в основном историческое. Многие ранние компиляторы C вообще не поддерживали плавающую точку. Он был добавлен позже, да и то не так полно - в основном был добавлен тип данных и самые примитивные операции, поддерживаемые языком, но все остальное оставлено стандартной библиотеке.
источник
Оператор по модулю
%
в C и C ++ определен для двух целых чисел, однако естьfmod()
функция, доступная для использования с числами типа double.источник
sin(fmod(x,3.14))
или дажеsin(fmod(x,M_PI))
не равноsin(x)
для больших значенийx
. Фактически значения могут отличаться на целых 2,0.%
оператор остатка и не оператор по модулю?Ограничения указаны в стандартах:
C11 (ISO / IEC 9899: 201x) §6.5.5 Мультипликативные операторы
C ++ 11 (ISO / IEC 14882: 2011) §5.6 Мультипликативные операторы
Решение состоит в том, чтобы использовать
fmod
, и именно поэтому операнды%
ограничены целочисленным типом в первую очередь, согласно C99 Rationale §6.5.5 Мультипликативные операторы :источник
пытаться
fmod
источник
Оператор% дает вам НАПОМИНАНИЕ (другое название модуля) числа. Для C / C ++ это определено только для целочисленных операций. Python немного шире и позволяет вам получить остаток от числа с плавающей запятой для остатка от того, сколько раз число может быть разделено на него:
>>> 4 % math.pi 0.85840734641020688 >>> 4 - math.pi 0.85840734641020688 >>>
источник
:P
%
Оператор не работает в C ++, когда вы пытаетесь найти остаток от двух чисел , которые оба типаFloat
илиDouble
.Следовательно, вы можете попробовать использовать
fmod
функцию изmath.h
/cmath.h
или вы можете использовать эти строки кода, чтобы избежать использования этого файла заголовка: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 ));
}
источник
«Математическое понятие арифметики по модулю работает также и для значений с плавающей запятой, и это одна из первых проблем, которые Дональд Кнут обсуждает в своей классической книге« Искусство компьютерного программирования »(том I). То есть когда-то это было базовое знание».
Оператор модуля с плавающей запятой определяется следующим образом:
m = num - iquot*den ; where iquot = int( num/den )
Как указано, отсутствие операции% оператора для чисел с плавающей запятой, по-видимому, связано со стандартами. CRTL предоставляет "fmod" и обычно также "остаток" для выполнения% на числах fp. Разница между этими двумя способами заключается в том, как они обрабатывают промежуточное округление iquot.
«Остаток» использует округление до ближайшего, а «fmod» использует простое усечение.
Если вы пишете свои собственные числовые классы C ++, ничто не мешает вам изменить устаревшее безоперационное значение, включив перегруженный оператор%.
С уважением
источник