mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/bigdecimal] Optimize rb_float_convert_to_BigDecimal by using dtoa
This improve the conversion speed several times faster than before. ``` RUBYLIB= BUNDLER_ORIG_RUBYLIB= /home/mrkn/.rbenv/versions/3.0.0/bin/ruby -v -S benchmark-driver /home/mrkn/src/github.com/ruby/bigdecimal/benchmark/from_float.yml ruby 3.0.0p0 (2020-12-25 revision95aff21468
) [x86_64-linux] Calculating ------------------------------------- bigdecimal 3.0.0 master flt_e0 156.400k 783.356k i/s - 100.000k times in 0.639388s 0.127656s flt_ep10 158.640k 777.978k i/s - 100.000k times in 0.630359s 0.128538s flt_ep100 101.676k 504.259k i/s - 100.000k times in 0.983512s 0.198311s flt_em10 103.439k 726.339k i/s - 100.000k times in 0.966751s 0.137677s flt_em100 79.675k 651.446k i/s - 100.000k times in 1.255095s 0.153505s Comparison: flt_e0 master: 783355.6 i/s bigdecimal 3.0.0: 156399.5 i/s - 5.01x slower flt_ep10 master: 777977.6 i/s bigdecimal 3.0.0: 158639.7 i/s - 4.90x slower flt_ep100 master: 504259.4 i/s bigdecimal 3.0.0: 101676.5 i/s - 4.96x slower flt_em10 master: 726338.6 i/s bigdecimal 3.0.0: 103439.2 i/s - 7.02x slower flt_em100 master: 651446.3 i/s bigdecimal 3.0.0: 79675.3 i/s - 8.18x slower ```5bdaedd530
9bfff57f90
d071a0abbb
This commit is contained in:
parent
2175c2c957
commit
4ba3a4491e
7 changed files with 3602 additions and 5 deletions
|
@ -74,7 +74,7 @@ static ID id_half;
|
|||
#define BASE1 (BASE/10)
|
||||
|
||||
#ifndef DBLE_FIG
|
||||
#define DBLE_FIG rmpd_double_figures() /* figure of double */
|
||||
#define DBLE_FIG RMPD_DOUBLE_FIGURES /* figure of double */
|
||||
#endif
|
||||
|
||||
#define LOG10_2 0.3010299956639812
|
||||
|
@ -205,6 +205,7 @@ cannot_be_coerced_into_BigDecimal(VALUE exc_class, VALUE v)
|
|||
}
|
||||
|
||||
static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
|
||||
static VALUE rb_inum_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
|
||||
static VALUE rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
|
||||
static VALUE rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
|
||||
static VALUE rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception);
|
||||
|
@ -2824,8 +2825,118 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
|
|||
rb_raise(rb_eArgError, "precision too large.");
|
||||
}
|
||||
|
||||
val = rb_funcall(val, id_to_r, 0);
|
||||
return rb_rational_convert_to_BigDecimal(val, digs, raise_exception);
|
||||
/* Use the same logic in flo_to_s to convert a float to a decimal string */
|
||||
char buf[DBLE_FIG + BASE_FIG + 2 + 1];
|
||||
int decpt, negative_p;
|
||||
char *e;
|
||||
char *p = BigDecimal_dtoa(d, 2, digs, &decpt, &negative_p, &e);
|
||||
int len10 = (int)(e - p);
|
||||
if (len10 >= (int)sizeof(buf))
|
||||
len10 = (int)sizeof(buf) - 1;
|
||||
memcpy(buf, p, len10);
|
||||
xfree(p);
|
||||
|
||||
VALUE inum;
|
||||
size_t RB_UNUSED_VAR(prec) = 0;
|
||||
size_t exp = 0;
|
||||
if (decpt > 0) {
|
||||
if (decpt < len10) {
|
||||
/*
|
||||
* len10 |---------------|
|
||||
* : |-------| frac_len10 = len10 - decpt
|
||||
* decpt |-------| |--| ntz10 = BASE_FIG - frac_len10 % BASE_FIG
|
||||
* : : :
|
||||
* 00 dd dddd.dddd dd 00
|
||||
* prec |-----.----.----.-----| prec = exp + roomof(frac_len, BASE_FIG)
|
||||
* exp |-----.----| exp = roomof(decpt, BASE_FIG)
|
||||
*/
|
||||
const size_t frac_len10 = len10 - decpt;
|
||||
const size_t ntz10 = BASE_FIG - frac_len10 % BASE_FIG;
|
||||
memset(buf + len10, '0', ntz10);
|
||||
buf[len10 + ntz10] = '\0';
|
||||
inum = rb_cstr_to_inum(buf, 10, false);
|
||||
|
||||
exp = roomof(decpt, BASE_FIG);
|
||||
prec = exp + roomof(frac_len10, BASE_FIG);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* decpt |-----------------------|
|
||||
* len10 |----------| :
|
||||
* : |------------| exp10
|
||||
* : : :
|
||||
* 00 dd dddd dd 00 0000 0000.0
|
||||
* : : : :
|
||||
* : |--| ntz10 = exp10 % BASE_FIG
|
||||
* prec |-----.----.-----| :
|
||||
* : |----.----| exp10 / BASE_FIG
|
||||
* exp |-----.----.-----.----.----|
|
||||
*/
|
||||
const size_t exp10 = decpt - len10;
|
||||
const size_t ntz10 = exp10 % BASE_FIG;
|
||||
|
||||
memset(buf + len10, '0', ntz10);
|
||||
buf[len10 + ntz10] = '\0';
|
||||
inum = rb_cstr_to_inum(buf, 10, false);
|
||||
|
||||
prec = roomof(len10 + ntz10, BASE_FIG);
|
||||
exp = prec + exp10 / BASE_FIG;
|
||||
}
|
||||
}
|
||||
else if (decpt == 0) {
|
||||
/*
|
||||
* len10 |------------|
|
||||
* : :
|
||||
* 0.dddd dddd dd 00
|
||||
* : : :
|
||||
* : |--| ntz10 = prec * BASE_FIG - len10
|
||||
* prec |----.----.-----| roomof(len10, BASE_FIG)
|
||||
*/
|
||||
prec = roomof(len10, BASE_FIG);
|
||||
const size_t ntz10 = prec * BASE_FIG - len10;
|
||||
|
||||
memset(buf + len10, '0', ntz10);
|
||||
buf[len10 + ntz10] = '\0';
|
||||
inum = rb_cstr_to_inum(buf, 10, false);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* len10 |---------------|
|
||||
* : :
|
||||
* decpt |-------| |--| ntz10 = prec * BASE_FIG - nlz10 - len10
|
||||
* : : :
|
||||
* 0.0000 00 dd dddd dddd dd 00
|
||||
* : : :
|
||||
* nlz10 |--| : decpt % BASE_FIG
|
||||
* prec |-----.----.----.-----| roomof(decpt + len10, BASE_FIG) - exp
|
||||
* exp |----| decpt / BASE_FIG
|
||||
*/
|
||||
decpt = -decpt;
|
||||
|
||||
const size_t nlz10 = decpt % BASE_FIG;
|
||||
exp = decpt / BASE_FIG;
|
||||
prec = roomof(decpt + len10, BASE_FIG) - exp;
|
||||
const size_t ntz10 = prec * BASE_FIG - nlz10 - len10;
|
||||
|
||||
if (nlz10 > 0) {
|
||||
memmove(buf + nlz10, buf, len10);
|
||||
memset(buf, '0', nlz10);
|
||||
}
|
||||
memset(buf + nlz10 + len10, '0', ntz10);
|
||||
buf[nlz10 + len10 + ntz10] = '\0';
|
||||
inum = rb_cstr_to_inum(buf, 10, false);
|
||||
|
||||
exp = -exp;
|
||||
}
|
||||
|
||||
VALUE bd = rb_inum_convert_to_BigDecimal(inum, SIZE_MAX, raise_exception);
|
||||
Real *vp;
|
||||
TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
|
||||
assert(vp->Prec == prec);
|
||||
vp->exponent = exp;
|
||||
|
||||
if (negative_p) VpSetSign(vp, -1);
|
||||
return bd;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
|
|
@ -21,7 +21,9 @@ Gem::Specification.new do |s|
|
|||
ext/bigdecimal/bigdecimal.h
|
||||
ext/bigdecimal/bits.h
|
||||
ext/bigdecimal/feature.h
|
||||
ext/bigdecimal/missing.c
|
||||
ext/bigdecimal/missing.h
|
||||
ext/bigdecimal/missing/dtoa.c
|
||||
ext/bigdecimal/static_assert.h
|
||||
lib/bigdecimal.rb
|
||||
lib/bigdecimal/jacobian.rb
|
||||
|
@ -40,5 +42,5 @@ Gem::Specification.new do |s|
|
|||
s.add_development_dependency "rake", ">= 12.3.3"
|
||||
s.add_development_dependency "rake-compiler", ">= 0.9"
|
||||
s.add_development_dependency "minitest", "< 5.0.0"
|
||||
s.add_development_dependency "pry"
|
||||
s.add_development_dependency "irb"
|
||||
end
|
||||
|
|
|
@ -77,6 +77,7 @@ extern VALUE rb_cBigDecimal;
|
|||
# define RMPD_BASE ((DECDIG)100U)
|
||||
#endif
|
||||
|
||||
#define RMPD_DOUBLE_FIGURES (1+DBL_DIG)
|
||||
|
||||
/*
|
||||
* NaN & Infinity
|
||||
|
@ -175,7 +176,7 @@ rmpd_base_value(void) { return RMPD_BASE; }
|
|||
static inline size_t
|
||||
rmpd_component_figures(void) { return RMPD_COMPONENT_FIGURES; }
|
||||
static inline size_t
|
||||
rmpd_double_figures(void) { return 1+DBL_DIG; }
|
||||
rmpd_double_figures(void) { return RMPD_DOUBLE_FIGURES; }
|
||||
|
||||
#define VpBaseFig() rmpd_component_figures()
|
||||
#define VpDblFig() rmpd_double_figures()
|
||||
|
|
|
@ -66,6 +66,7 @@ have_func("llabs", "stdlib.h")
|
|||
have_func("finite", "math.h")
|
||||
have_func("isfinite", "math.h")
|
||||
|
||||
have_header("ruby/atomic.h")
|
||||
have_header("ruby/internal/has/builtin.h")
|
||||
have_header("ruby/internal/static_assert.h")
|
||||
|
||||
|
|
17
ext/bigdecimal/missing.c
Normal file
17
ext/bigdecimal/missing.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <ruby/ruby.h>
|
||||
|
||||
#ifdef HAVE_RUBY_ATOMIC_H
|
||||
# include <ruby/atomic.h>
|
||||
#endif
|
||||
|
||||
#ifdef RUBY_ATOMIC_PTR_CAS
|
||||
# define ATOMIC_PTR_CAS(var, old, new) RUBY_ATOMIC_PTR_CAS(var, old, new)
|
||||
#endif
|
||||
|
||||
#undef strtod
|
||||
#define strtod BigDecimal_strtod
|
||||
#undef dtoa
|
||||
#define dtoa BigDecimal_dtoa
|
||||
#undef hdtoa
|
||||
#define hdtoa BigDecimal_hdtoa
|
||||
#include "missing/dtoa.c"
|
|
@ -117,6 +117,9 @@ finite(double)
|
|||
# endif
|
||||
#endif
|
||||
|
||||
/* dtoa */
|
||||
char *BigDecimal_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve);
|
||||
|
||||
/* rational */
|
||||
|
||||
#ifndef HAVE_RB_RATIONAL_NUM
|
||||
|
|
3462
ext/bigdecimal/missing/dtoa.c
Normal file
3462
ext/bigdecimal/missing/dtoa.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue