python负十进制、十进制和正整数的模运算
使用简单的python负十进制、十进制和正整数的模运算,python,python-3.x,decimal,Python,Python 3.x,Decimal,使用简单的ints: >>> -45 % 360 315 鉴于,使用十进制.decimal: >>> from decimal import Decimal >>> Decimal('-45') % 360 Decimal('-45') 我希望得到十进制('315') 这有什么原因吗?是否有一种方法可以获得一致的行为(无需修补decimal.decimal)?(我没有改变上下文,也找不到如何改变上下文来解决这种情况)。经过长时间的搜索(因
int
s:
>>> -45 % 360
315
鉴于,使用十进制.decimal
:
>>> from decimal import Decimal
>>> Decimal('-45') % 360
Decimal('-45')
我希望得到十进制('315')
这有什么原因吗?是否有一种方法可以获得一致的行为(无需修补decimal.decimal
)?(我没有改变上下文,也找不到如何改变上下文来解决这种情况)。经过长时间的搜索(因为搜索“%”、“mod”、“modulo”等会得到上千个结果),我终于意外地发现:
十进制对象上的算术运算之间有一些小的差异
以及整数和浮点数的算术运算。当余数运算符%
应用于十进制对象时,结果的符号为
除数的符号,而不是除数的符号:
>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')
我不知道这样做的原因,但看起来不可能改变这种行为(不打补丁)。Python的行为符合IBM的标准 定义如下: 余数取两个操作数;它返回整数除法的余数。[……] 结果是计算整除运算(如所述)后的被除数余数,必要时四舍五入为精确数字。如果结果的符号不为零,则与原始股息的符号相同 因此,因为
Decimal('-45')//D('360')
是Decimal('-0')
,所以余数只能是Decimal('-45')
为什么商是0而不是-1?报告说:
除以整数取两个操作数;它将两个数字相除并返回结果的整数部分。[……]
返回的结果定义为当股息大于或等于除数时,从股息中重复减去除数的结果。在这个减法过程中,使用了被除数和除数的绝对值:最终结果的符号与使用正常除法的结果相同。[……]
注:[……]
至于为什么十进制规范采用这种方法,而不是像在数学中那样,余数总是正的,我推测这可能是因为减法算法的简单性。计算商的绝对值不需要检查操作数的符号。无论如何,现代实现可能使用更复杂的算法,但在标准形成、硬件更简单(晶体管更少)的年代,简单性可能是一个重要因素。有趣的事实:英特尔仅在2007年发布时才从基数2整数除法转换为基数16。十进制是否使用
math.fmod()
?他们都有相同的行为“因此,在处理浮点时,通常首选函数fmod(),而在处理整数时,则首选Python的x%y。”@Piintesky根据我在库源代码中的理解,它确实将数学作为_math导入,但是\u math仅用于从浮点
构建一个十进制
实例\uuuuMod\uuuuuuuuu
(以及其他方法,如\uuuuuuTrueDiv\uuuuu
)通过调用Decimal.\uDivide()
计算商和余数,该方法依赖于调用整数的divmod
。然而,它并没有产生相同的结果。。。请注意:divmod(-45360)=(-1315)
但是divmod(十进制('-45')、十进制('360'))==(十进制('-0')、十进制('-45'))
。我认为@Mr.T提到fmod是正确的。即使Decimal
的实现根本不涉及fmod
,但其目的似乎至少是为了模拟fmod
的行为。很抱歉进行了编辑。我原以为添加一个摘录就足以说明问题,但后来我发现它有点复杂,所以我添加了我自己的答案。你让它听起来像修补是一件极端的事情,需要避免,但像Python这样的语言在你自己的代码(子类、神奇的“dunder”方法、包装器等)中给了你很大的空间来做这件事。当然,你也可以做一些简单但有效的工作,编写你自己的函数来代替内置的语法。@JohnY对不起,我不是故意这么说的;我的意图是让问题不被理解为“如何修补Decimal以获得此类行为?”,而是“是否存在此类行为的原因?”以及“是否有任何已实施的方法来更改此行为(例如,通过上下文设置)?”。也许我应该提到我已经在需要的地方实现了一个小的变通方法。