<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>西瓜田 &#187; python</title>
	<atom:link href="http://blog.thpiano.com/?feed=rss2&#038;tag=python" rel="self" type="application/rss+xml" />
	<link>http://blog.thpiano.com</link>
	<description>无复洛城东</description>
	<lastBuildDate>Tue, 19 Jan 2021 03:54:37 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>为什么负数取余操作（%）在c和python下结果不同</title>
		<link>http://blog.thpiano.com/?p=1023</link>
		<comments>http://blog.thpiano.com/?p=1023#comments</comments>
		<pubDate>Sun, 19 Jul 2015 15:32:55 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[div]]></category>
		<category><![CDATA[mod]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[不同]]></category>
		<category><![CDATA[余数]]></category>
		<category><![CDATA[取余]]></category>
		<category><![CDATA[取模]]></category>

		<guid isPermaLink="false">http://blog.thpiano.com/?p=1023</guid>
		<description><![CDATA[最近在用python，偶尔有一次涉及到了负数取余操作： -3 % 4 按照之前写c/c++、java的习惯，这里肯定是等于-3。但是python下返回的结果竟然是1！ 为什么会有不同的表现呢？先从取余操作本身说起： &#160; 关于取余(modulo operation) 取余的定义 由于不同的架构有不同的数字表示法和运算法，因此他们对于取余的定义也有可能略有不同。 一般来说，公认的取余操作（被除数a、除数n、商q以及余数r，n % a = r）需要满足以下三条： 然而这个定义是非常宽泛的。以 -3 % 4为例： -3既可以表示为：-3 = 4 * -1 + 1 也可以表示为：-3 = 4 * 0 + (-3) 这两种表示法分别对应了两种实现方法：Truncated Division及 Floored Division。两种实现方法的求商方式不同，导致其最终结果也不同。 Truncated Division 在Truncated Division下，求商时，采用的是q = trunc(a/n) ，即将算数除法结果的小数部分移除，余下的整数部分作为商。 （也有一种更形象的说法，除法取整时，是朝着靠近0的方向取整） 以-3 % 4为例： q = trunc(-3/4) = trunc(-0.75) = 0 因此，余数r = [...]]]></description>
			<content:encoded><![CDATA[<p>最近在用python，偶尔有一次涉及到了负数取余操作： -3 % 4</p>
<p>按照之前写c/c++、java的习惯，这里肯定是等于<span style="color: #0000ff;"><strong>-3</strong></span>。但是python下返回的结果竟然是<span style="color: #0000ff;"><strong>1</strong></span>！</p>
<p>为什么会有不同的表现呢？先从取余操作本身说起：</p>
<p>&nbsp;</p>
<h1>关于取余(modulo operation)</h1>
<h4>取余的定义</h4>
<p>由于不同的架构有不同的数字表示法和运算法，因此他们对于取余的定义也有可能略有不同。</p>
<p>一般来说，公认的取余操作（被除数<em>a</em>、除数<em>n</em>、商<em>q</em>以及余数<em>r</em>，<em>n</em> % <em>a</em> = <em>r</em>）需要满足以下三条：</p>
<table>
<tbody>
<tr>
<td><img src="https://upload.wikimedia.org/math/2/1/9/21923d0bed7d824da52419c946093e48.png" alt="\begin{align}&lt;br /&gt;&lt;br /&gt; q \,&amp;\in \mathbb{Z} \\&lt;br /&gt;&lt;br /&gt; a \,&amp;= n q + r \\&lt;br /&gt;&lt;br /&gt; |r| \,&amp;&lt; |n|.&lt;br /&gt;&lt;br /&gt; \end{align}" /></td>
</tr>
</tbody>
</table>
<p>然而这个定义是非常宽泛的。以 -3 % 4为例：</p>
<p>-3既可以表示为：-3 = 4 * -1 + 1</p>
<p>也可以表示为：-3 = 4 * 0 + (-3)</p>
<p>这两种表示法分别对应了两种实现方法：Truncated Division及 Floored Division。两种实现方法的求商方式不同，导致其最终结果也不同。<span id="more-1023"></span></p>
<h4>Truncated Division</h4>
<p>在Truncated Division下，求商时，采用的是<em>q</em> = trunc(<em>a</em>/<em>n</em>) ，即将算数除法结果的小数部分移除，余下的整数部分作为商。</p>
<p>（也有一种更形象的说法，除法取整时，是朝着靠近0的方向取整）</p>
<p>以-3 % 4为例：</p>
<p><em>q</em> = trunc(<em>-3</em>/<em>4</em>) = trunc(-0.75) = 0</p>
<p>因此，余数r = a - nq = -3 - (0 * 4) = -3</p>
<p>此实现有如下特点：</p>
<p>1. 余数的符号与<strong><span style="color: #0000ff;">被除数</span></strong>相同</p>
<p>2. 这种实现在某些地方被称之为<strong><span style="color: #0000ff;">取模</span></strong></p>
<p>&nbsp;</p>
<h4>Floored Division</h4>
<p>在Floored Division下，求商时，采用的是<em>q</em> = ⌊<em>a</em>/<em>n</em>⌋，即将算数除法结果进行向下取整作为商。</p>
<p>以-3 % 4为例：</p>
<p><em>q</em> = ⌊<em>-3</em>/<em>4</em>⌋ = ⌊<em>-0.75</em>⌋ = -1</p>
<p>因此，余数r = a - nq = -3 - (-1 * 4) = 1</p>
<p>此实现有如下特点：</p>
<p>1. 余数的符号与<strong><span style="color: #0000ff;">除数</span></strong>相同</p>
<p>2. 这种实现在某些地方被称之为<strong><span style="color: #0000ff;">求余</span></strong></p>
<p>&nbsp;</p>
<h1>关于语言标准</h1>
<p>由上可知，存在着多种实现方式，那么具体地，c和python是如何选取的呢？</p>
<h4>C语言标准中关于取余运算的说明</h4>
<p>C89中，并未做严格规定：</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;"><p>When integers are divided and the division is inexact, if both operands are positive the result of the/ operator is the largest integer less than the algebraic quotient and the result of the % operator is positive. <strong>If either operand is negative</strong>, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is <strong>implementation-defined</strong>, as is the sign of the result of the % operator. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.</p></blockquote>
<p>而C99，明确指定了需要使用Truncated Version：</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;"><p>When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.<sup>88)</sup> If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.</p>
<p><sub>88) This is often called "truncation toward zero".</sub></p></blockquote>
<h4>Python标准中关于取余运算的说明</h4>
<p>Python 2：从说明中关于余数符号的部分，可以推断出其指定了Floored Version</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;">
<p id="index-55">The <tt>%</tt> (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the <a title="exceptions.ZeroDivisionError" href="https://docs.python.org/2/library/exceptions.html#exceptions.ZeroDivisionError"><tt>ZeroDivisionError</tt></a> exception. The arguments may be floating point numbers, e.g., <tt>3.14%0.7</tt> equals <tt>0.34</tt> (since <tt>3.14</tt> equals <tt>4*0.7 + 0.34</tt>.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand <a id="id10" href="https://docs.python.org/2/reference/expressions.html#id21">[2]</a>.</p>
<p>The integer division and modulo operators are connected by the following identity: <tt>x == (x/y)*y + (x%y)</tt>. Integer division and modulo are also connected with the built-in function <a title="divmod" href="https://docs.python.org/2/library/functions.html#divmod"><tt>divmod()</tt></a>: <tt>divmod(x, y) == (x/y,x%y)</tt>. These identities don’t hold for floating point numbers; there similar identities hold approximately where <tt>x/y</tt> is replaced by <tt>floor(x/y)</tt> or <tt>floor(x/y) - 1</tt> <a id="id11" href="https://docs.python.org/2/reference/expressions.html#id22">[3]</a>.</p></blockquote>
<p>Python 3：和2一样，可推断出指定Floored Version</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;">
<p id="index-51">The <tt>%</tt> (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the <a title="ZeroDivisionError" href="https://docs.python.org/3/library/exceptions.html#ZeroDivisionError"><tt>ZeroDivisionError</tt></a> exception. The arguments may be floating point numbers, e.g., <tt>3.14%0.7</tt> equals <tt>0.34</tt> (since <tt>3.14</tt> equals <tt>4*0.7 + 0.34</tt>.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand <a id="id8" href="https://docs.python.org/3/reference/expressions.html#id16">[1]</a>.</p>
<p>The floor division and modulo operators are connected by the following identity: <tt>x == (x//y)*y + (x%y)</tt>. Floor division and modulo are also connected with the built-in function <a title="divmod" href="https://docs.python.org/3/library/functions.html#divmod"><tt>divmod()</tt></a>: <tt>divmod(x, y) == (x//y, x%y)</tt>. <a id="id9" href="https://docs.python.org/3/reference/expressions.html#id17">[2]</a>.</p></blockquote>
<h1>结论</h1>
<div> c/c++标准中指定了采用Truncated Version进行实现，而Python标准中指定了采用Floored Version进行实现，因此造成了结果上的不同。</div>
<p>&nbsp;</p>
<h1>一些推断及其他</h1>
<p>最后，来小小地猜测一下，为什么c语言和python会选取不同的实现方案。</p>
<h4>对于c：</h4>
<p>在X86架构下，对于整数的除法和取余操作，都是通过同一个指令进行的（有符号除法使用idiv，而无符号为div），商和余数会同时作为结果返回在寄存器AX和DX中。</p>
<p>这个特性被c的库函数divmod所利用，导致编译器有可能会将除法及取余操作都优化为使用一个指令完成。</p>
<p>这样一来，通过如下三条：</p>
<p>1. c的除法正好为trunc除（即Truncated Version中取商的方式）</p>
<p>2. 通常程序里除法出现的频率都远远高过取余的频率，取余可能会被优化为一个指令</p>
<p>3. c语言设计风格为性能和简洁优先，而c++又要与c兼容</p>
<p>因此c语言采用了Truncated Version。</p>
<p>而这也为一个常用的判断埋下了坑：</p>
<p>如果你想判断一个数是否是奇数，</p>
<div class="codecolorer-container c geshi" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bool isEven<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> x<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #b1b100;">return</span> x <span style="color: #339933;">%</span> <span style="color: #0000dd;">2</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>会埋下大坑（对负数存在bug）</p>
<p>而需要写成</p>
<div class="codecolorer-container c geshi" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bool isEven<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> x<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #b1b100;">return</span> x <span style="color: #339933;">%</span> <span style="color: #0000dd;">2</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<h4>对于python：</h4>
<p>不会像c那样将性能放到第一位，会比c有更多的语法糖，更贴近常用思维，因此使用Floored Version会更加适合。</p>
<p>&nbsp;</p>
<h1>参考文献</h1>
<p>https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation</p>
<p>https://docs.python.org/2/reference/expressions.html#arithmetic-conversions</p>
<p>http://stackoverflow.com/questions/3609572/does-either-ansi-c-or-iso-c-specify-what-5-10-should-be</p>
<p>http://stackoverflow.com/questions/11630321/why-does-c-output-negative-numbers-when-using-modulo</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=1023</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
