[Bug #17037] Improve accuracy of division near precision limits

When dividing near the precision limit of `double`, use Bignum
division to get rid of rounding errors.
This commit is contained in:
Nobuyoshi Nakada 2023-11-29 20:16:36 +09:00
Родитель 79eb75a8dd
Коммит 8e93bf8e1f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 3582D74E1FEE4465
2 изменённых файлов: 16 добавлений и 2 удалений

Просмотреть файл

@ -4096,7 +4096,13 @@ static double
fix_fdiv_double(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
return double_div_double(FIX2LONG(x), FIX2LONG(y));
long iy = FIX2LONG(y);
#if SIZEOF_LONG * CHAR_BIT > DBL_MANT_DIG
if ((iy < 0 ? -iy : iy) >= (1L << DBL_MANT_DIG)) {
return rb_big_fdiv_double(rb_int2big(FIX2LONG(x)), rb_int2big(iy));
}
#endif
return double_div_double(FIX2LONG(x), iy);
}
else if (RB_BIGNUM_TYPE_P(y)) {
return rb_big_fdiv_double(rb_int2big(FIX2LONG(x)), y);
@ -4114,7 +4120,7 @@ rb_int_fdiv_double(VALUE x, VALUE y)
{
if (RB_INTEGER_TYPE_P(y) && !FIXNUM_ZERO_P(y)) {
VALUE gcd = rb_gcd(x, y);
if (!FIXNUM_ZERO_P(gcd)) {
if (!FIXNUM_ZERO_P(gcd) && gcd != INT2FIX(1)) {
x = rb_int_idiv(x, gcd);
y = rb_int_idiv(y, gcd);
}

Просмотреть файл

@ -704,6 +704,14 @@ class TestInteger < Test::Unit::TestCase
def test_fdiv
assert_equal(1.0, 1.fdiv(1))
assert_equal(0.5, 1.fdiv(2))
m = 50 << Float::MANT_DIG
prev = 1.0
(1..100).each do |i|
val = (m + i).fdiv(m)
assert_operator val, :>=, prev, "1+epsilon*(#{i}/100)"
prev = val
end
end
def test_obj_fdiv