如何在C/C++/Obj-C中编写一个能处理负数的取模(%)运算符

92

作为一名数学家,我对C衍生语言之一的一个小恶习是

(-1) % 8 // comes out as -1, and not 7

fmodf(-1,8) // fails similarly

最佳解决方案是什么?

C++允许使用模板和操作符重载,但这两者对我来说都是比较深奥的领域。欢迎提供示例。


1
我认为这不完全是 https://dev59.com/-XRA5IYBdhLWcg3wyBD1 的“重复”。根据官方定义,这个问题的答案不能合并到那个问题中,因为这个问题只问模数,而不涉及除法。但我认为这个问题已经被那个问题覆盖了,所以很接近。我的答案已经在那里了。 - Steve Jessop
也许应该将那个线程拆分,因为它提出了两个不同的问题。最好的方法可能是单独重新提出分割问题,然后将其指向该答案。我会把这个任务留给更了解这个网站机制的人。 - P i
3
@Pi owhere在哪里说“%”是“模数”...它是“余数”。 - obataku
1
以下是关于“%”问题的参考链接,这也是一个与此问题“重复”的线程:https://dev59.com/X3NA5IYBdhLWcg3wH6EW - leetNightshade
如果你只是在进行2的幂次数除法,那么使用按位与符号&可能是更好的选择:(-1) & 8 == 7 - Henricus V.
@Henry W. (-1) & 8 == 7(-1) & 8只会产生0或8,而不是7。也许你的意思是(-1) & (8-1) - chux - Reinstate Monica
16个回答

1
/* 警告:宏 mod 对其参数的副作用进行多次评估。 */ #define mod(r,m) (((r) % (m)) + ((r)<0)?(m):0)

... 或者只是习惯获得等价类的任何代表。


2
“习惯于获取等价类的任何代表”?那是无稽之谈。如果您想要这样做,可以直接使用原始的“代表”r运算符与等价类无关。它是余数运算符,而余数在代数上被定义为非负且小于除数。不幸的是,C语言将其定义错误了。尽管如此,对于提供最佳答案,我还是给予+1的评价。 - R.. GitHub STOP HELPING ICE

0

C++的示例模板

template< class T >
T mod( T a, T b )
{
    T const r = a%b;
    return ((r!=0)&&((r^b)<0) ? r + b : r);
}

使用此模板,返回的余数将为零或与除数(分母)具有相同的符号(相当于向负无穷舍入),而不是 C++ 的行为,即余数为零或与被除数(分子)具有相同的符号(相当于向零舍入)。

-1

mod为正数时,此解决方案避免了负除法或余数运算:

int core_modulus(int val, int mod)
{
    if(val>=0)
        return val % mod;
    else
        return val + mod * ((mod - val - 1)/mod);
}

-1
define  MOD(a, b)       ((((a)%(b))+(b))%(b))

这个代码可以运行,但是像这样将其定义为宏实在是太丑陋了。下面是一个模板版本:https://dev59.com/NEzSa4cB1Zd3GeqPrf8F#2581867 - leetNightshade

-1
unsigned mod(int a, unsigned b) {
    return (a >= 0 ? a % b : b - (-a) % b);
}

-2

我会这样做:

((-1)+8) % 8 

在进行模运算之前,将后一个数字加到第一个数字上,从而得到所需的7。这对于任何数字都应该有效,最小可达-8。对于-9,添加2*8。


2
那么对于一个值可能为“-99999”的变量呢? - Keith Thompson
就我个人而言,我认为这是一个很好的答案。例如,如果您只需要处理-1,那么您可以添加模数一次。 - user4945014

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接