Open3

Ribbon: 割り算を修正する

okuokuokuoku

流石にひととおりのテストは通しておきたいので。。

Test: 116/125 passed.

Failed:
    [~qq_63]  Expected: -3  Actual: -2
    [~rr_62]  Expected: 1  Actual: -1
    [(floor-remainder -5 2)]  Expected: 1  Actual: -1
    [(floor-quotient -5 2)]  Expected: -3  Actual: -2
    [~qq_63]  Expected: -3  Actual: -2
    [~rr_62]  Expected: -1  Actual: 1
    [(floor-remainder 5 -2)]  Expected: -1  Actual: 1
    [(floor-quotient 5 -2)]  Expected: -3  Actual: -2
    [(integer? (/ 1 3))]  Expected: #f  Actual: #t
okuokuokuoku

(/ 1 3)

... いきなり悩みどころだな。。このテストは消すことにした。

https://github.com/okuoku/yuni/issues/180

整数だよ派

C言語では (int)(1 / 3) = (int)0 なので、 / がC言語的な除算とするならば 現状の (integer? (/ 1 3)) → 真 は期待値と言える。

しかし、RibbonはSchemeで、かつ、R7RS Schemeでは 整数 / 整数 → 整数 となるような / を全パターン標準化しているのでちょっと何とも言えない挙動と言える。

他の言語だとRubyがここに属する。

$ irb
irb(main):001:0> 1/3
=> 0

1/3 だよ派

有理数(ratnum)を実装した通常のSchemeは (/ 1 3)1/3 となる。

$ gosh
gosh> (/ 1 3)
1/3

有理数はPythonやRubyのような多くの言語でサポートされている。

$ python
Python 3.9.10 (main, Jan 20 2022, 21:37:52)
[GCC 11.2.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from fractions import Fraction
>>> Fraction(1)/3
Fraction(1, 3)
$ irb
irb(main):001:0> Rational(1)/3
=> (1/3)

0.3333... だよ派

0.333... だよ派もある。身近なところでは電卓であるとか、Pythonがこの派閥に属する。

$ python
Python 3.9.10 (main, Jan 20 2022, 21:37:52)
[GCC 11.2.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 1/3
0.3333333333333333

JavaScriptには整数が無いので自然とこの派閥になる。

$ node
Welcome to Node.js v18.16.0.
Type ".help" for more information.
> 1/3
0.3333333333333333
okuokuokuoku

floor-quotientfloor-remainder

floor-quotient はPythonで言うところの // に相当する。これに負を与えたときの挙動が間違っている。

(import (yuni scheme))

(call-with-values
  (lambda () (floor/ -5 2))
  (lambda (qq rr)
    (write (list 'QQ: qq 'RR: rr)) (newline)
    (write (list 'Q: (floor-quotient -5 2) 'R: (floor-remainder -5 2))) (newline)
    )
  )

のようなコードが、

  • Ribbon
(QQ: -2 RR: -1)
(Q: -2 R: -1)
  • Gauche
(QQ: -3 RR: 1)
(Q: -3 R: 1)

Ribbonの挙動はコレ truncate/ だな。。

https://github.com/okuoku/ribbon/blob/2fc99b2bcf30909544947a32ce911c352e9f60a8/c-proto/prims.inc.h#L2500-L2501

C99以降のC言語の除算は"Truncate toward to zero"なので、floorしたければ手動で除算する必要がある。

https://github.com/okuoku/ribbon/commit/346d1399895d4e46760474d3869d50fa4fdf3677

... うーん。。結局2段階にした。

  1. 符号が一致した割り算は floor/truncate/ の間には差は無いのでそのまま /% する
  2. 符号が一致しない場合は、まず余りを求め、余りぶんを減算してから商を求める。余りの符号は適当に揃える。

後者の方法で答えが出る正確で簡単な説明を思いつかない。