ごとけんです

In message "[ruby-math:00138] Re: %  &  divmod() & remainder()"
    on 00/01/27, Yukihiro Matsumoto <matz / netlab.co.jp> writes:

>私は小学校時代に実数を含む割算で余りとか習わなかったんですが。
>あと、負の整数の割算の余りも。

それはもっともな意見です。これらには正式な定義などないと思い
ます。divmod の用途に基づいて決めるのが良いでしょう。んで、
ちょっと考えてみました。

たぶん実数のdivmodの用途で一番多いのは、f(0) = 0 であるよう
な周期関数f(x)の(位)相を求めるようなことだと思います。たとえ
ばsinを例にとると、2pi = 2*Math::PI として、

x          -4pi -3pi -2pi  -pi   0    pi  2pi  3pi 4pi
          ---|----|----|----|----|----|----|----|---|---
位相      .. 0   pi    0   pi    0   pi    0   pi   0 ..
何周期目  .. [   -2   )[   -1   )[    0   )[   1   )[   ..

また、割る数が負の場合は上の表の x の向きが逆転していると考
えるわけです(ただし割る数も同時に-1倍する)。

以上より、d,m = x.divmod(y) に関する条件として次を提案します。

  (y<=>0)*x == m + (y<=>0)*y*d

-- gotoken

# おまけ: 全くひねりの無いRubyによる定義とそのテスト

class Numeric def dm(y) # alternative divmod return [y,y] unless y == y # [NaN, NaN] d = self/y # to raise ZeroDivisionError return [d,d*y-self] if y.zero? x,y = (y<0 ? [-self,-y] : [self,y]) d = x-x+y-y # suitable zero if x.zero? # d = 0 elsif x > 0 d += 1 while y*d < x-y elsif x < 0 d -= 1 while y*d >= x else return [x, x] end m = x - d*y return [d, m] end end class Float alias fucomp <=> # def <=>(x) # Eguchi-san's <=> if x == x and self == self fucomp(x) elsif x == x self else x end end end ### test for dm ### if __FILE__ == $0 def cond0(x,y,d,m) x == y*d + m end # def cond1(x,y,d,m) # equivalent to cond2 # if y >= 0 # x == y*d + m # else # -x == -y*d + m # end # end def cond2(x,y,d,m) (y<=>0)*x == (y<=>0)*y*d + m end def dmtest(x,y) # trap("SIGINT"){p "dmtest(#{x.inspect},#{y.inspect})"; return} fmt = "%x %y | [%div, %mod] | %5s | %5s" (x.is_a? Float) ? fmt.gsub!("x","4.1f") : fmt.gsub!("x","4d") (y.is_a? Float) ? fmt.gsub!("y","4.1f") : fmt.gsub!("y","4d") begin d,m = x.dm(y) d ||= 0; m ||= 0 (d.is_a? Float) ? fmt.gsub!("div","4.1f") : fmt.gsub!("div","4d") (m.is_a? Float) ? fmt.gsub!("mod","4.1f") : fmt.gsub!("mod","4d") format(fmt, x,y,d,m,cond0(x,y,d,m), cond2(x,y,d,m)) rescue ZeroDivisionError fmt.gsub!(/\|.*/, "# ZeroDivisionError") format(fmt, x,y) end end Inf = 1.0/0.0 NaN = 0.0/0.0 X = [-Inf,-2,0,2,Inf,NaN] Y = [-Inf,-3,-2,-1,0,1,2,3,Inf,NaN] X.each{|n| puts "## #{n}" Y.each{|d| x,y = n,d puts dmtest(x,y) x,y = n.to_f, d puts dmtest(x,y) x,y = n, d.to_f puts dmtest(x,y) x,y = n.to_f, d.to_f puts dmtest(x,y) } } end