summaryrefslogtreecommitdiff
path: root/numeric.c
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2023-11-29 20:16:36 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2023-11-29 20:16:36 +0900
commit8e93bf8e1fbac73b677c333b19a8b55ae9daddc3 (patch)
tree92a47d3fe8bb981af5a476cd4a30e4f7e2a0997b /numeric.c
parent79eb75a8dd64848f23e9efc465f06326b5d4b680 (diff)
[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.
Diffstat (limited to 'numeric.c')
-rw-r--r--numeric.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/numeric.c b/numeric.c
index 8cea384d8f..0fd1802601 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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);
}