まあ,説明するのも面倒なのですが,学校で習った四捨五入は「不適切な方法」なので,忘れましょう。
JIS Z 8401(数値の丸め方)(PDF、75KB)
https://www.erca.go.jp/fukakin/before/pdf/suuchi.pdf
あなたが思ったのと違った結果になっても,それは,「あなた(学校教育あるいは文部科学省)が間違っている(不適切だ)から」なのです(残念!!!)。
まあ,説明するのも面倒なのですが,学校で習った四捨五入は「不適切な方法」なので,忘れましょう。
JIS Z 8401(数値の丸め方)(PDF、75KB)
https://www.erca.go.jp/fukakin/before/pdf/suuchi.pdf
あなたが思ったのと違った結果になっても,それは,「あなた(学校教育あるいは文部科学省)が間違っている(不適切だ)から」なのです(残念!!!)。
R の round の example
> example(round)
round> round(.5 + -2:4) # IEEE / IEC rounding: -2 0 0 2 2 4 4
[1] -2 0 0 2 2 4 4
round> ## (this is *good* behaviour -- do *NOT* report it as bug !)
round>
round> ( x1 <- seq(-2, 4, by = .5) )
[1] -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
round> round(x1) #-- IEEE / IEC rounding !
[1] -2 -2 -1 0 0 0 1 2 2 2 3 4 4
round> x1[trunc(x1) != floor(x1)]
[1] -1.5 -0.5
round> x1[round(x1) != floor(x1 + .5)]
[1] -1.5 0.5 2.5
round> (non.int <- ceiling(x1) != floor(x1))
[1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE
round> x2 <- pi * 100^(-1:3)
round> round(x2, 3)
[1] 0.031 3.142 314.159 31415.927 3141592.654
round> signif(x2, 3)
[1] 3.14e-02 3.14e+00 3.14e+02 3.14e+04 3.14e+06
Python で同じことを書いてみる。
>>> np.around(0.5 + np.arange(-2, 5))
array([-2., -0., 0., 2., 2., 4., 4.])
>>> x1 = np.arange(-2, 4.5, step=0.5)
>>> x1
array([-2. , -1.5, -1. , -0.5, 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. ,
3.5, 4. ])
>>> np.around(x1)
array([-2., -2., -1., -0., 0., 0., 1., 2., 2., 2., 3., 4., 4.])
>>> x1[np.trunc(x1) != np.floor(x1)]
array([-1.5, -0.5])
>>> x1[np.around(x1) != np.floor(x1 + 0.5)]
array([-1.5, 0.5, 2.5])
>>> non_int = np.ceil(x1) != np.floor(x1)
>>> non_int
array([False, True, False, True, False, True, False, True, False,
True, False, True, False])
>>> x2 = np.pi * 100.0**(np.arange(-1, 4))
>>> np.around(x2, 3)
array([3.10000000e-02, 3.14200000e+00, 3.14159000e+02, 3.14159270e+04,
3.14159265e+06])
>>> print(np.around(x2, 3))
[3.10000000e-02 3.14200000e+00 3.14159000e+02 3.14159270e+04
3.14159265e+06]
>>> print(*np.around(x2, 3))
0.031 3.142 314.159 31415.927 3141592.654
R の signif を Python の関数として書く。
>>> def signif(x, dig):
... return np.around(x, dig - int(np.ceil(np.log10(abs(x)))))
R のようには,いわゆる「科学的浮動小数点表示」にはならない。
>>> result = [signif(x, 3) for x in x2]
>>> print(*result)
0.0314 3.14 314.0 31400.0 3140000.0
Julia で同じことを書いてみる。
結果を整数 Int にしないと -0.0 が出現する。
round.(0.5 .+ -2:4) |> println # [-2.0, -0.0, 0.0, 2.0, 2.0, 4.0]
round.(Int, 0.5 .+ -2:4) |> println # [-2, 0, 0, 2, 2, 4]
x1 = -2:0.5:4
round.(x1) |> println # [-2.0, -2.0, -1.0, -0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0]
x1[trunc.(x1) .!= floor.(x1)] |> println # [-1.5, -0.5]
x1[round.(x1) .!= floor.(x1 .+ 0.5)] |> println # [-1.5, 0.5, 2.5]
non_int = ceil.(x1) .!= floor.(x1)
non_int |> println # Bool[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
x2 = π .* 100.0 .^(-1:3)
round.(x2, digits=3) |> println # [0.031, 3.142, 314.159, 31415.927, 3.141592654e6]
Julia にも R の signif 関数を定義する。
signif(x, dig) = round(x, digits=dig - ceil(Int, log10(abs(x))))
最後の数字のように,巨大数になると「科学的浮動小数点表示」になるが,これは print 関数の働きの結果であろう。
signif.(x2, 3) |> println # [0.0314, 3.14, 314.0, 31400.0, 3.14e6]
Python 公式ドキュメントに
> 注釈 浮動小数点数に対する round() の振る舞いは意外なものかもしれません: 例えば、 round(2.675, 2) は予想通りの 2.68 ではなく 2.67 を与えます。これはバグではありません: これはほとんどの小数が浮動小数点数で正確に表せないことの結果です。詳しくは 浮動小数点演算、その問題と制限 を参照してください。
と書いてあるそうだ。この説明は正確ではない。
2.5 も 3.5 も「浮動小数点数で正確に表せる」が,round の結果は異なる。
>>> round(2.5)
2
>>> round(3.5)
4
digits != 0
) and may be surprising. Be aware that most decimal fractions are not exactly representable in binary double precision. In R 4.0.0, the algorithm for round(x, d)
, for d>0d>0
, has been improved to measure and round “to nearest even”, contrary to earlier versions of R (or also to sprintf()
or format()
based rounding).
と書いているように,「四捨五入をしようとしたとき,結果(の末尾)が偶数になるように丸める」のである。
つまり,2.5 を「学校で習った四捨五入」をすると 3 になるのであるが,これは奇数なので 2 に丸める(2.5 は 2 と 3 のちょうど真ん中なので,2 に丸めても 3 に丸めてもよいのだけど結果が偶数になるように 2 に丸めるということ)。
3.5 を「学校で習った四捨五入」をすると 4 になるのであるが,これは偶数なので 4 のままにする。
Julia でも,being rounded to the nearest even integer と書いてある。
The RoundingMode r controls the direction of the rounding; the default is RoundNearest, which rounds to the nearest integer, with ties (fractional values of 0.5) being rounded to the nearest even integer. Note that round may give incorrect results if the global rounding mode is changed (see rounding).
こうなると,Python はどうなんだ?と思う。help(np.around) で表示される文書に,ちゃんと書いてある(np.round より np.around をつかえといわれる)。
For values exactly halfway between rounded decimal values, NumPy rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0, -0.5 and 0.5 round to 0.0, etc.
これに続いて更に詳しく書かれている。ぜひちゃんと読むべし。
冒頭に掲げた,寝言(の原文)はどこに書いてあるのかな?