ruby-changes:53426
From: shyouhei <ko1@a...>
Date: Fri, 9 Nov 2018 18:14:28 +0900 (JST)
Subject: [ruby-changes:53426] shyouhei:r65642 (trunk): numeric.c: avoid division by zero
shyouhei 2018-11-09 18:14:23 +0900 (Fri, 09 Nov 2018) New Revision: 65642 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65642 Log: numeric.c: avoid division by zero In C, division by zero is undefined, even if the expression is double (cf: ISO/IEC 9899:1990 section 6.3.5). OTOH we have tests about such operations and results, means we expect no exceptional situation shall occur. We need to carefully reroute the situation, and generate what is needed. See also: https://travis-ci.org/ruby/ruby/jobs/452680646#L2943 PS: Recently (last two decades), C have Annex. F document. It normatively specifies that the division operator is IEEE 754's division operator (cf: ISO/IEC 9899:1999 section F.3). If we could move to such newer version this could be no problem. But that is not possible today. Modified files: trunk/numeric.c Index: numeric.c =================================================================== --- numeric.c (revision 65641) +++ numeric.c (revision 65642) @@ -1083,6 +1083,12 @@ flo_mul(VALUE x, VALUE y) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1083 } } +static bool +flo_iszero(VALUE f) +{ + return RFLOAT_VALUE(f) == 0.0; +} + /* * call-seq: * float / other -> float @@ -1093,23 +1099,52 @@ flo_mul(VALUE x, VALUE y) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1099 static VALUE flo_div(VALUE x, VALUE y) { - long f_y; - double d; + double den; + double num = RFLOAT_VALUE(x); + double sign = 1.0; if (RB_TYPE_P(y, T_FIXNUM)) { - f_y = FIX2LONG(y); - return DBL2NUM(RFLOAT_VALUE(x) / (double)f_y); + if (FIXNUM_ZERO_P(y)) { + goto zerodiv; + } + else { + den = FIX2LONG(y); + goto nonzero; + } } else if (RB_TYPE_P(y, T_BIGNUM)) { - d = rb_big2dbl(y); - return DBL2NUM(RFLOAT_VALUE(x) / d); + if (rb_bigzero_p(y)) { + goto zerodiv; + } + else { + den = rb_big2dbl(y); + goto nonzero; + } } else if (RB_TYPE_P(y, T_FLOAT)) { - return DBL2NUM(RFLOAT_VALUE(x) / RFLOAT_VALUE(y)); + if (flo_iszero(y)) { + sign = signbit(RFLOAT_VALUE(y)) ? -1.0 : 1.0; + goto zerodiv; + } + else { + den = RFLOAT_VALUE(y); + goto nonzero; + } } else { return rb_num_coerce_bin(x, y, '/'); } + +nonzero: + return DBL2NUM(num / den); + +zerodiv: + if (num == 0.0) { + return DBL2NUM(nan("")); + } + else { + return DBL2NUM(num * sign * HUGE_VAL); + } } /* @@ -1676,10 +1711,7 @@ rb_float_abs(VALUE flt) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1711 static VALUE flo_zero_p(VALUE num) { - if (RFLOAT_VALUE(num) == 0.0) { - return Qtrue; - } - return Qfalse; + return flo_iszero(num) ? Qtrue : Qfalse; } /* -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/