Modulo of negative integers in Go

huangapple go评论72阅读模式
英文:

Modulo of negative integers in Go

问题

我正在学习Go语言,我之前是使用Python的。

最近,我发现Go语言中的取模运算符%的行为与Python中的对应运算符有所不同。与模运算和余数的定义相反,负整数对正整数取模的结果是一个负值。

例如:

Python

a, b, n = -5, 5, 3
for i in range(a, b):
    print(i%n)    

输出:

1
2
0
1
2
0
1
2
0
1

Go

a, b, n := -5, 5, 3
for i:=a; i<b; i++ {
    fmt.Println(i%n)
}

输出:

-2
-1
0
-2
-1
0
1
2
0
1

在阅读了关于取模运算符和一些类似的问题后,我了解到这些差异是由于相关语言的设计目标所致。

Go语言中是否有内置功能可以复制Python中的取模运算?

另外:是否有一种内部方法可以计算“模数”而不是“余数”?

英文:

I am learning Go and I come from a Python background.

Recently, I stumbled onto a behaviour of the %(modulo) operator which is different from the corresponding operator in Python. Quite contrary to the definition of modular operation and remainder, the modulus of negative integers by a positive integer returns a negative value.

Example:

Python

a, b, n = -5, 5, 3
for i in range(a, b):
    print(i%n)    

Output:

1
2
0
1
2
0
1
2
0
1

Go

a, b, n := -5, 5, 3
for i:=a; i<b; i++ {
    fmt.Println(i%n)
}

Output:

-2
-1
0
-2
-1
0
1
2
0
1

After reading about the Modulo operator and few similar questions asked about the reason behind these differences, I understand that these were due to design goals of the concerned languages.

Is there a built-in functionality in Go which replicates the modulus operation of Python?

Alternate: Is there an internal method for computing the "modulus" instead of the "remainder"?

答案1

得分: 19

请注意,以下是翻译的内容:

参见语言设计者之一的这条评论

> 目前的定义有几个原因:
>
> - 当前的%运算符语义直接来自x86架构的结果
> - 改变基本运算符%的含义而不改变其名称会令人困惑
> - 从%的结果计算另一个模数相当容易
>
> 请注意,%计算的是“余数”,而不是“模数”。

标准库中没有一个运算符或函数可以复制Python的模数运算。

可以编写一个函数来复制Python的模数运算:

<!-- language: lang-go -->

func modLikePython(d, m int) int {
   var res int = d % m
   if ((res &lt; 0 &amp;&amp; m &gt; 0) || (res &gt; 0 &amp;&amp; m &lt; 0)) {
      return res + m
   }
   return res
}

请注意,在Python中,5 % -3的结果是-1,这段代码也复制了这种行为。如果你不想要这个行为,请在if语句的||后面删除第二部分。

英文:

See this comment by one of the language designers:

> There are a several reasons for the current definition:
>
> - the current semantics for % is directly available as a result from x86 architectures
> - it would be confusing to change the meaning of the elementary operator % and not change its name
> - it's fairly easy to compute another modulus from the % result
>
> Note that % computes the "remainder" as opposed to the "modulus".

There is not an operator or function in the standard library which replicates the modulus operation of Python.

It is possible to write a function which replicates the modulus operation of Python:

<!-- language: lang-go -->

func modLikePython(d, m int) int {
   var res int = d % m
   if ((res &lt; 0 &amp;&amp; m &gt; 0) || (res &gt; 0 &amp;&amp; m &lt; 0)) {
      return res + m
   }
   return res
}

Note that in Python 5 % -3 is -1 and this code replicates that behavior as well. If you don't want that, remove the second part after || in the if statement.

答案2

得分: 16

有没有一种内部方法可以计算“模数”而不是“余数”?

请注意,%运算符计算的是“余数”,而不是“模数”。

这些引号有点误导人。

查阅任何“模数”的定义,大多数情况下都会说它是除法后的余数。问题在于,当我们说“余数”时,它意味着只有一个余数。当涉及负数时,可能存在多个不同的余数。在维基百科的Remainder页面上,它区分了“最小正余数”和“最小绝对余数”。你还可以添加一个“最小负余数”(最小负数意味着负数,但最接近0)。

通常情况下,对于模数运算符,如果返回一个正值,它就是“最小正余数”,如果返回一个负值,它就是“最小负余数”。返回值的符号可以通过多种方式确定。例如,给定c = a mod b,你可以定义c的符号为:

  • a的符号(Go中的%运算符所做的)
  • b的符号(Python中的%运算符所做的)
  • 总是非负

这是一份编程语言及其按照这种方式定义的模数实现的列表:https://en.wikipedia.org/wiki/Modulo_operation#In_programming_languages

这是一种无分支的方法,用于复制Python的%运算符的Go函数

func mod(a, b int) int {
	return (a % b + b) % b
}

再次强调,这遵循以下规则:

给定c = a mod bc的符号将与b的符号相同。
换句话说,模数的结果与除数具有相同的符号。

英文:

> Is there an internal method for computing the "modulus" instead of the "remainder"?

> Note that % computes the "remainder" as opposed to the "modulus".

These quotes are a bit misleading.

Look up any definition of "modulo", by and large it will say that it is the remainder after division. The problem is that when we say "the remainder", it implies that there is only one. When negative numbers are involved, there can be more than one distinct remainder. On the Wikipedia page for Remainder, it differentiates between the least positive remainder and the least absolute remainder. You could also add a least negative remainder (least negative meaning negative, but closest to 0).

Generally for modulus operators, if it returned a positive value, it was the least positive remainder and if it returned a negative value, it was the least negative remainder. The sign of the returned value can be determined in multiple ways. For example given c = a mod b, you could define the sign of c to be

  • The sign of a (what % does in Go)
  • The sign of b (what % does in Python)
  • Non-negative always

Here's a list of programming languages and their modulo implementations defined in this way https://en.wikipedia.org/wiki/Modulo_operation#In_programming_languages

Here's a branchless way to replicate Python's % operator with a Go function

func mod(a, b int) int {
	return (a % b + b) % b
}

To reiterate, this follows the rule:
> given c = a mod b, the sign of c will be the sign of b.
Or in other words, the modulus result has the same sign as the divisor

答案3

得分: 0

math/big包提供了欧几里得取模运算:

package main

import "math/big"

func mod(x, y int64) int64 {
   bx, by := big.NewInt(x), big.NewInt(y)
   return new(big.Int).Mod(bx, by).Int64()
}

func main() {
   z := mod(-5, 3)
   println(z == 1)
}

更多信息请参考:https://golang.org/pkg/math/big#Int.Mod

英文:

math/big does Euclidean modulus:

package main
import &quot;math/big&quot;

func mod(x, y int64) int64 {
   bx, by := big.NewInt(x), big.NewInt(y)
   return new(big.Int).Mod(bx, by).Int64()
}

func main() {
   z := mod(-5, 3)
   println(z == 1)
}

https://golang.org/pkg/math/big#Int.Mod

答案4

得分: 0

在Q2中,你可以使用以下代码:

func modNeg(v, m int) int {
    return (v%m + m) % m
}

输出结果为:

modNeg(-1, 5) => 4
modNeg(-2, 3) => 0
英文:

On Q2, you could use:

func modNeg(v, m int) int {
    return (v%m + m) % m
}

Would output:

modNeg(-1, 5) =&gt; 4
modNeg(-2, 3) =&gt; 0

答案5

得分: 0

在大多数情况下,只需将第二个数字添加到结果中:

Python:

-8%6 => 4

Golang:

-8%6 + 6 => 4

因此,函数将如下所示:

func PyMod(d int, m int) int {
  d %= m
  if d < 0 { 
    d += m
  }
  return d
}

它适用于其他一些情况,例如a%-b,除了-a%b

但是,如果您希望它甚至适用于-a%-b,请按照以下方式操作:

func PyMod(d int, m int) int {
  // 在顶部添加此条件
  if d < 0 && m < 0 {
	return d % m
  } 
  d %= m
  if d < 0 { 
	d += m
  }
  return d
}
英文:

In most cases, just add the second number to the result:

Python:

-8%6 =&gt; 4

Golang:

-8%6 + 6 =&gt; 4

So the function will be like this:

func PyMod(d int, m int) int {
  d %= m
  if d &lt; 0 { 
    d += m
  }
  return d
}

It works for some other situations such as a%-b in addition to -a%b.

But if you want it to work even for -a%-b, do like this:

func PyMod(d int, m int) int {
  // Add this condition at the top
  if d &lt; 0 &amp;&amp; m &lt; 0 {
	return d % m
  } 
  d %= m
  if d &lt; 0 { 
	d += m
  }
  return d
}

huangapple
  • 本文由 发表于 2017年3月25日 23:26:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/43018206.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定