From 260ad015338650016e057cd03cdb1b6950ef92c0 Mon Sep 17 00:00:00 2001 From: Sean Collins Date: Fri, 21 May 2021 12:57:50 -0600 Subject: [PATCH 01/33] Include Ruby 2.7 in table (It wasn't released when this table was first written, but it still applies) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a062bb71..927cce11 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The differences among versions are given below: | ------- | --------------- | ----------------------- | | 3.0.0 | You can use BigDecimal with Ractor on Ruby 3.0 | 2.5 .. | | 2.0.x | You cannot use BigDecimal.new and do subclassing | 2.4 .. | -| 1.4.x | BigDecimal.new and subclassing always prints warning. | 2.3 .. 2.6 | +| 1.4.x | BigDecimal.new and subclassing always prints warning. | 2.3 .. 2.7 | | 1.3.5 | You can use BigDecimal.new and subclassing without warning | .. 2.5 | You can select the version you want to use using `gem` method in Gemfile or scripts. From 70146fb6ada66eb0ae418603ac61e5e7d68314b3 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 14 Jan 2022 00:52:11 +0900 Subject: [PATCH 02/33] Adjust a local variable type to exponent --- ext/bigdecimal/bigdecimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index fc74c0be..97510fad 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3281,7 +3281,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) VALUE inum; size_t RB_UNUSED_VAR(prec) = 0; - size_t exp = 0; + SIGNED_VALUE exp = 0; if (decpt > 0) { if (decpt < len10) { /* From a18522e9cac13f85946bd25352c1397103482958 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 13 Apr 2022 13:30:31 -0400 Subject: [PATCH 03/33] Fix docs rdoc parses f[i] as a link, which results in a broken link. --- lib/bigdecimal/jacobian.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bigdecimal/jacobian.rb b/lib/bigdecimal/jacobian.rb index 5e293042..4448024c 100644 --- a/lib/bigdecimal/jacobian.rb +++ b/lib/bigdecimal/jacobian.rb @@ -42,8 +42,8 @@ def isEqual(a,b,zero=0.0,e=1.0e-8) end - # Computes the derivative of f[i] at x[i]. - # fx is the value of f at x. + # Computes the derivative of +f[i]+ at +x[i]+. + # +fx+ is the value of +f+ at +x+. def dfdxi(f,fx,x,i) nRetry = 0 n = x.size @@ -75,7 +75,7 @@ def dfdxi(f,fx,x,i) deriv end - # Computes the Jacobian of f at x. fx is the value of f at x. + # Computes the Jacobian of +f+ at +x+. +fx+ is the value of +f+ at +x+. def jacobian(f,fx,x) n = x.size dfdx = Array.new(n*n) From 3ede8860a6f12df0a7fab0b7f82eb84a1471b176 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 23 Apr 2022 14:45:08 -0500 Subject: [PATCH 04/33] Correct indentation in Kernel#BigDecimal --- ext/bigdecimal/bigdecimal.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 97510fad..b3ef70a2 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3493,12 +3493,12 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) * BigDecimal(value, exception: true) -> bigdecimal * BigDecimal(value, ndigits, exception: true) -> bigdecimal * - * Returns the \BigDecimal converted from +value+ - * with a precision of +ndigits+ decimal digits. + * Returns the \BigDecimal converted from +value+ + * with a precision of +ndigits+ decimal digits. * - * When +ndigits+ is less than the number of significant digits - * in the value, the result is rounded to that number of digits, - * according to the current rounding mode; see BigDecimal.mode. + * When +ndigits+ is less than the number of significant digits + * in the value, the result is rounded to that number of digits, + * according to the current rounding mode; see BigDecimal.mode. * * Returns +value+ converted to a \BigDecimal, depending on the type of +value+: * From 8ecf99719bf2c178a7bc96d3db31a5d1375c9932 Mon Sep 17 00:00:00 2001 From: Thomas Winsnes Date: Tue, 5 Jul 2022 10:44:46 +1000 Subject: [PATCH 05/33] Updated license information so it matches and the license file and it can now be picked up by automatic license checking tools --- LICENSE.txt => LICENSE | 0 bigdecimal.gemspec | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename LICENSE.txt => LICENSE (100%) diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/bigdecimal.gemspec b/bigdecimal.gemspec index 2ed7d093..89101f2d 100644 --- a/bigdecimal.gemspec +++ b/bigdecimal.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Arbitrary-precision decimal floating-point number library." s.description = "This library provides arbitrary-precision decimal floating-point number class." s.homepage = "https://github.com/ruby/bigdecimal" - s.license = "Ruby" + s.license = "bsd-2-clause" s.require_paths = %w[lib] s.extensions = %w[ext/bigdecimal/extconf.rb] From 175bbacd43df8929850c0d83772e59c9d6557439 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 14 Jul 2022 15:17:35 +0900 Subject: [PATCH 06/33] Remove checks for `struct RRational` and `struct RComplex` These are used to see only if `RRATIONAL` and `RCOMPLEX` are available, however, these two are macros and can be checked with `#ifdef` directly. --- ext/bigdecimal/extconf.rb | 2 -- ext/bigdecimal/missing.h | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index 9b0c55b2..4920374b 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -67,10 +67,8 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_header("ruby/internal/has/builtin.h") have_header("ruby/internal/static_assert.h") -have_type("struct RRational", "ruby.h") have_func("rb_rational_num", "ruby.h") have_func("rb_rational_den", "ruby.h") -have_type("struct RComplex", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") have_func("rb_array_const_ptr", "ruby.h") diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 79698491..49b7c766 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -126,7 +126,7 @@ char *BigDecimal_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, c static inline VALUE rb_rational_num(VALUE rat) { -#ifdef HAVE_TYPE_STRUCT_RRATIONAL +#ifdef RRATIONAL return RRATIONAL(rat)->num; #else return rb_funcall(rat, rb_intern("numerator"), 0); @@ -138,7 +138,7 @@ rb_rational_num(VALUE rat) static inline VALUE rb_rational_den(VALUE rat) { -#ifdef HAVE_TYPE_STRUCT_RRATIONAL +#ifdef RRATIONAL return RRATIONAL(rat)->den; #else return rb_funcall(rat, rb_intern("denominator"), 0); @@ -152,7 +152,7 @@ rb_rational_den(VALUE rat) static inline VALUE rb_complex_real(VALUE cmp) { -#ifdef HAVE_TYPE_STRUCT_RCOMPLEX +#ifdef RCOMPLEX return RCOMPLEX(cmp)->real; #else return rb_funcall(cmp, rb_intern("real"), 0); @@ -164,7 +164,7 @@ rb_complex_real(VALUE cmp) static inline VALUE rb_complex_imag(VALUE cmp) { -# ifdef HAVE_TYPE_STRUCT_RCOMPLEX +# ifdef RCOMPLEX return RCOMPLEX(cmp)->imag; # else return rb_funcall(cmp, rb_intern("imag"), 0); From c55480b2060cefbffc78a11e50fbd50d2f8ab5e3 Mon Sep 17 00:00:00 2001 From: Thomas Winsnes Date: Tue, 2 Aug 2022 02:22:22 +0200 Subject: [PATCH 07/33] Updated to use multiple licenses Co-authored-by: Hiroshi SHIBATA --- bigdecimal.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigdecimal.gemspec b/bigdecimal.gemspec index 89101f2d..96e1aa83 100644 --- a/bigdecimal.gemspec +++ b/bigdecimal.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Arbitrary-precision decimal floating-point number library." s.description = "This library provides arbitrary-precision decimal floating-point number class." s.homepage = "https://github.com/ruby/bigdecimal" - s.license = "bsd-2-clause" + s.license = ["Ruby", "bsd-2-clause"] s.require_paths = %w[lib] s.extensions = %w[ext/bigdecimal/extconf.rb] From 13165b29b8f13b1132f5ec22eab2077b54937acc Mon Sep 17 00:00:00 2001 From: Thomas Winsnes Date: Tue, 2 Aug 2022 02:23:34 +0200 Subject: [PATCH 08/33] Updated to use the correct spec for muilti license --- bigdecimal.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigdecimal.gemspec b/bigdecimal.gemspec index 96e1aa83..1feed332 100644 --- a/bigdecimal.gemspec +++ b/bigdecimal.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Arbitrary-precision decimal floating-point number library." s.description = "This library provides arbitrary-precision decimal floating-point number class." s.homepage = "https://github.com/ruby/bigdecimal" - s.license = ["Ruby", "bsd-2-clause"] + s.licenses = ["Ruby", "bsd-2-clause"] s.require_paths = %w[lib] s.extensions = %w[ext/bigdecimal/extconf.rb] From 741fb8df4e130b136f51acb2e461dea8509fbbc9 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 8 Sep 2022 14:31:45 +0900 Subject: [PATCH 09/33] Fixed filenames for test-suite from ruby/ruby --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index e8667eb2..8fd7c14b 100644 --- a/Rakefile +++ b/Rakefile @@ -47,8 +47,8 @@ task :sync_tool do require 'fileutils' sync_files = [ - "../ruby/tool/lib/test/unit/core_assertions.rb", - "../ruby/tool/lib/test/unit/core_assertions.rb", + "../ruby/tool/lib/core_assertions.rb", + "../ruby/tool/lib/find_executable.rb", "../ruby/tool/lib/envutil.rb" ] From 0b6645ffae6262573af2e6843ca99c0ced4b53ec Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 8 Sep 2022 14:32:27 +0900 Subject: [PATCH 10/33] Update the latest version of core_assertions.rb --- test/lib/core_assertions.rb | 163 +++++++++++++++++------------------- test/lib/envutil.rb | 7 +- 2 files changed, 84 insertions(+), 86 deletions(-) diff --git a/test/lib/core_assertions.rb b/test/lib/core_assertions.rb index a4abff41..7cd598b1 100644 --- a/test/lib/core_assertions.rb +++ b/test/lib/core_assertions.rb @@ -3,6 +3,10 @@ module Test module Unit module Assertions + def assert_raises(*exp, &b) + raise NoMethodError, "use assert_raise", caller + end + def _assertions= n # :nodoc: @_assertions = n end @@ -16,31 +20,24 @@ def _assertions # :nodoc: def message msg = nil, ending = nil, &default proc { - msg = msg.call.chomp(".") if Proc === msg - custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? - "#{custom_message}#{default.call}#{ending || "."}" + ending ||= (ending_pattern = /(? e - bt = e.backtrace - as = e.instance_of?(MiniTest::Assertion) - if as - ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o - bt.reject! {|ln| ans =~ ln} - end - if ((args.empty? && !as) || - args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a }) - msg = message(msg) { - "Exception raised:\n<#{mu_pp(e)}>\n" + - "Backtrace:\n" + - e.backtrace.map{|frame| " #{frame}"}.join("\n") - } - raise MiniTest::Assertion, msg.call, bt - else - raise - end + rescue *(args.empty? ? Exception : args) => e + msg = message(msg) { + "Exception raised:\n<#{mu_pp(e)}>\n""Backtrace:\n" << + Test.filter_backtrace(e.backtrace).map{|frame| " #{frame}"}.join("\n") + } + raise Test::Unit::AssertionFailedError, msg.call, e.backtrace end end @@ -259,11 +242,11 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt) ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") - def separated_runner(out = nil) + def separated_runner(token, out = nil) include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) out = out ? IO.new(out, 'w') : STDOUT at_exit { - out.puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}" + out.puts "#{token}", [Marshal.dump($!)].pack('m'), "#{token}", "#{token}assertions=#{self._assertions}" } Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) if defined?(Test::Unit::Runner) end @@ -275,22 +258,24 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o line ||= loc.lineno end capture_stdout = true - unless /mswin|mingw/ =~ RUBY_PLATFORM + unless /mswin|mingw/ =~ RbConfig::CONFIG['host_os'] capture_stdout = false - opt[:out] = MiniTest::Unit.output if defined?(MiniTest::Unit) + opt[:out] = Test::Unit::Runner.output if defined?(Test::Unit::Runner) res_p, res_c = IO.pipe opt[:ios] = [res_c] end + token_dump, token_re = new_test_token src = <\n\K.*\n(?=#{token_re}<\/error>$)/m].unpack1("m")) rescue => marshal_error ignore_stderr = nil res = nil @@ -402,8 +387,8 @@ def assert_raise(*exp, &b) begin yield - rescue MiniTest::Skip => e - return e if exp.include? MiniTest::Skip + rescue Test::Unit::PendedError => e + return e if exp.include? Test::Unit::PendedError raise e rescue Exception => e expected = exp.any? { |ex| @@ -478,7 +463,7 @@ def assert_raise_with_message(exception, expected, msg = nil, &block) ex end - MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc: + TEST_DIR = File.join(__dir__, "test/unit") #:nodoc: # :call-seq: # assert(test, [failure_message]) @@ -498,7 +483,7 @@ def assert(test, *msgs) when nil msgs.shift else - bt = caller.reject { |s| s.start_with?(MINI_DIR) } + bt = caller.reject { |s| s.start_with?(TEST_DIR) } raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt end unless msgs.empty? super @@ -521,7 +506,7 @@ def assert_respond_to(obj, (meth, *priv), msg = nil) return assert obj.respond_to?(meth, *priv), msg end #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) return if obj.respond_to?(meth) end super(obj, meth, msg) @@ -544,7 +529,7 @@ def assert_not_respond_to(obj, (meth, *priv), msg = nil) return assert !obj.respond_to?(meth, *priv), msg end #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) return unless obj.respond_to?(meth) end refute_respond_to(obj, meth, msg) @@ -563,11 +548,13 @@ def assert_pattern_list(pattern_list, actual, message=nil) anchored = false else if anchored - match = /\A#{pattern}/.match(rest) + match = rest.rindex(pattern, 0) else - match = pattern.match(rest) + match = rest.index(pattern) end - unless match + if match + post_match = $~ ? $~.post_match : rest[match+pattern.size..-1] + else msg = message(msg) { expect_msg = "Expected #{mu_pp pattern}\n" if /\n[^\n]/ =~ rest @@ -584,7 +571,7 @@ def assert_pattern_list(pattern_list, actual, message=nil) } assert false, msg end - rest = match.post_match + rest = post_match anchored = true end } @@ -611,19 +598,20 @@ def assert_warn(*args) def assert_deprecated_warning(mesg = /deprecated/) assert_warning(mesg) do - Warning[:deprecated] = true + Warning[:deprecated] = true if Warning.respond_to?(:[]=) yield end end def assert_deprecated_warn(mesg = /deprecated/) assert_warn(mesg) do - Warning[:deprecated] = true + Warning[:deprecated] = true if Warning.respond_to?(:[]=) yield end end class << (AssertFile = Struct.new(:failure_message).new) + include Assertions include CoreAssertions def assert_file_predicate(predicate, *args) if /\Anot_/ =~ predicate @@ -655,7 +643,7 @@ def initialize def for(key) @count += 1 - yield + yield key rescue Exception => e @failures[key] = [@count, e] end @@ -709,15 +697,25 @@ def assert_join_threads(threads, message = nil) msg = "exceptions on #{errs.length} threads:\n" + errs.map {|t, err| "#{t.inspect}:\n" + - RUBY_VERSION >= "2.5.0" ? err.full_message(highlight: false, order: :top) : err.message + err.full_message(highlight: false, order: :top) }.join("\n---\n") if message msg = "#{message}\n#{msg}" end - raise MiniTest::Assertion, msg + raise Test::Unit::AssertionFailedError, msg end end + def assert_all?(obj, m = nil, &blk) + failed = [] + obj.each do |*a, &b| + unless blk.call(*a, &b) + failed << (a.size > 1 ? a : a[0]) + end + end + assert(failed.empty?, message(m) {failed.pretty_inspect}) + end + def assert_all_assertions(msg = nil) all = AllFailures.new yield all @@ -726,23 +724,13 @@ def assert_all_assertions(msg = nil) end alias all_assertions assert_all_assertions - def message(msg = nil, *args, &default) # :nodoc: - if Proc === msg - super(nil, *args) do - ary = [msg.call, (default.call if default)].compact.reject(&:empty?) - if 1 < ary.length - ary[0...-1] = ary[0...-1].map {|str| str.sub(/(? Date: Mon, 19 Sep 2022 10:15:04 -0400 Subject: [PATCH 11/33] Remove array defs in missing.h for old Rubies Commit 02b6053 added these to support Ruby 2.0.0. The rb_array_const_ptr function is defined since Ruby 2.3. --- ext/bigdecimal/extconf.rb | 1 - ext/bigdecimal/missing.h | 29 ----------------------------- 2 files changed, 30 deletions(-) diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index 4920374b..e74196e1 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -71,7 +71,6 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_func("rb_rational_den", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") -have_func("rb_array_const_ptr", "ruby.h") have_func("rb_sym2str", "ruby.h") have_func("rb_opts_exception_p", "ruby.h") have_func("rb_category_warn", "ruby.h") diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 49b7c766..02d2348e 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -172,35 +172,6 @@ rb_complex_imag(VALUE cmp) } #endif -/* array */ - -#ifndef FIX_CONST_VALUE_PTR -# if defined(__fcc__) || defined(__fcc_version) || \ - defined(__FCC__) || defined(__FCC_VERSION) -/* workaround for old version of Fujitsu C Compiler (fcc) */ -# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x)) -# else -# define FIX_CONST_VALUE_PTR(x) (x) -# endif -#endif - -#ifndef HAVE_RB_ARRAY_CONST_PTR -static inline const VALUE * -rb_array_const_ptr(VALUE a) -{ - return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? - RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr); -} -#endif - -#ifndef RARRAY_CONST_PTR -# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a) -#endif - -#ifndef RARRAY_AREF -# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i]) -#endif - /* symbol */ #ifndef HAVE_RB_SYM2STR From be366c9cf276dc7e07a1898b4843b349e25992e8 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 19 Sep 2022 10:23:29 -0400 Subject: [PATCH 12/33] Remove symbol defs in missing.h for old Rubies Commit 2885514 added these to support Ruby 2.1. The rb_sym2str function is defined since Ruby 2.2. --- ext/bigdecimal/extconf.rb | 1 - ext/bigdecimal/missing.h | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index e74196e1..17e7905d 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -71,7 +71,6 @@ def have_builtin_func(name, check_expr, opt = "", &b) have_func("rb_rational_den", "ruby.h") have_func("rb_complex_real", "ruby.h") have_func("rb_complex_imag", "ruby.h") -have_func("rb_sym2str", "ruby.h") have_func("rb_opts_exception_p", "ruby.h") have_func("rb_category_warn", "ruby.h") have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h") diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 02d2348e..307147c0 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -172,16 +172,6 @@ rb_complex_imag(VALUE cmp) } #endif -/* symbol */ - -#ifndef HAVE_RB_SYM2STR -static inline VALUE -rb_sym2str(VALUE sym) -{ - return rb_id2str(SYM2ID(sym)); -} -#endif - /* st */ #ifndef ST2FIX From 5415b120ab28bbd52566dc809a767a9b84403dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciek=20Rz=C4=85sa?= Date: Thu, 28 Jul 2022 14:30:10 +0200 Subject: [PATCH 13/33] Improve documentation of BigDecimal#sign Fixes https://github.com/ruby/bigdecimal/issues/78 by describing behaviour for positive and negative zero in the docs. --- ext/bigdecimal/bigdecimal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index b3ef70a2..06c8251c 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3606,8 +3606,10 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self) /* Returns the sign of the value. * - * Returns a positive value if > 0, a negative value if < 0, and a - * zero if == 0. + * Returns a positive value if > 0, a negative value if < 0. + * It behaves the same with zeros - + * it returns a positive value for a positive zero (BigDecimal('0')) and + * a negative value for a negative zero (BigDecimal('-0')). * * The specific value returned indicates the type and sign of the BigDecimal, * as follows: From 223d193f015bc5a3e8b57801a8e1482dc2df7e46 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 25 Sep 2022 23:34:19 +1300 Subject: [PATCH 14/33] Remove trailing whitespace. --- ext/bigdecimal/bigdecimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 06c8251c..61a8d4d8 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3607,9 +3607,9 @@ BigDecimal_limit(int argc, VALUE *argv, VALUE self) /* Returns the sign of the value. * * Returns a positive value if > 0, a negative value if < 0. - * It behaves the same with zeros - + * It behaves the same with zeros - * it returns a positive value for a positive zero (BigDecimal('0')) and - * a negative value for a negative zero (BigDecimal('-0')). + * a negative value for a negative zero (BigDecimal('-0')). * * The specific value returned indicates the type and sign of the BigDecimal, * as follows: From 4f0894c6c03dc13e0aa10b6d82a5483f21e148b1 Mon Sep 17 00:00:00 2001 From: Maciej Rzasa Date: Fri, 29 Jul 2022 11:33:23 +0200 Subject: [PATCH 15/33] Document precision=0 and ndigits=0 for converting from Float --- ext/bigdecimal/bigdecimal.c | 3 +++ lib/bigdecimal/util.rb | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 61a8d4d8..1483f327 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3500,6 +3500,9 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) * in the value, the result is rounded to that number of digits, * according to the current rounding mode; see BigDecimal.mode. * + * When +ndigits+ is 0, the number of digits to correctly represent a float number + * is determined automatically. + * * Returns +value+ converted to a \BigDecimal, depending on the type of +value+: * * - Integer, Float, Rational, Complex, or BigDecimal: converted directly: diff --git a/lib/bigdecimal/util.rb b/lib/bigdecimal/util.rb index cb645d2a..ad92f7cf 100644 --- a/lib/bigdecimal/util.rb +++ b/lib/bigdecimal/util.rb @@ -33,12 +33,16 @@ class Float < Numeric # # Returns the value of +float+ as a BigDecimal. # The +precision+ parameter is used to determine the number of - # significant digits for the result (the default is Float::DIG). + # significant digits for the result. When +precision+ is set to +0+, + # the number of digits to represent the float being converted is determined + # automatically. + # The default +precision+ is +0+. # # require 'bigdecimal' # require 'bigdecimal/util' # # 0.5.to_d # => 0.5e0 + # 1.234.to_d # => 0.1234e1 # 1.234.to_d(2) # => 0.12e1 # # See also BigDecimal::new. From cd35868aa6f58f9b2f05c181e964672970f19cb5 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 30 Oct 2022 22:21:18 +0900 Subject: [PATCH 16/33] Suppress macro redefinition warnings `HAVE_` macros by autoconf are defined as 1. --- ext/bigdecimal/missing.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/bigdecimal/missing.h b/ext/bigdecimal/missing.h index 307147c0..325554b5 100644 --- a/ext/bigdecimal/missing.h +++ b/ext/bigdecimal/missing.h @@ -35,10 +35,10 @@ extern "C" { #endif /* RB_UNUSED_VAR */ #if defined(_MSC_VER) && _MSC_VER >= 1310 -# define HAVE___ASSUME +# define HAVE___ASSUME 1 #elif defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1300 -# define HAVE___ASSUME +# define HAVE___ASSUME 1 #endif #ifndef UNREACHABLE From 4ecf04da7a77a5239430db273e5e1971ac99c64f Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 10:09:16 +0900 Subject: [PATCH 17/33] Make BigDecimal_double_fig inline --- ext/bigdecimal/bigdecimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 1483f327..51bef764 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -264,7 +264,7 @@ GetVpValue(VALUE v, int must) * BigDecimal.double_fig # => 16 * */ -static VALUE +static inline VALUE BigDecimal_double_fig(VALUE self) { return INT2FIX(VpDblFig()); From 1b642e2e593d4958dfe92ed7617a17be39743292 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 10:11:27 +0900 Subject: [PATCH 18/33] Make GetVpValue inline --- ext/bigdecimal/bigdecimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 51bef764..c8f55bce 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -249,7 +249,7 @@ GetVpValueWithPrec(VALUE v, long prec, int must) return NULL; /* NULL means to coerce */ } -static Real* +static inline Real* GetVpValue(VALUE v, int must) { return GetVpValueWithPrec(v, -1, must); From 91b72a93415c2b63b2919bd1472a7a90a7a6a516 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 10:13:54 +0900 Subject: [PATCH 19/33] [Doc] Fix the document of n_significant_digits --- ext/bigdecimal/bigdecimal.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index c8f55bce..20158c27 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -486,15 +486,15 @@ BigDecimal_precision_scale(VALUE self) * * Returns the number of decimal significant digits in +self+. * - * BigDecimal("0").scale # => 0 - * BigDecimal("1").scale # => 1 - * BigDecimal("1.1").scale # => 2 - * BigDecimal("3.1415").scale # => 5 - * BigDecimal("-1e20").precision # => 1 - * BigDecimal("1e-20").precision # => 1 - * BigDecimal("Infinity").scale # => 0 - * BigDecimal("-Infinity").scale # => 0 - * BigDecimal("NaN").scale # => 0 + * BigDecimal("0").n_significant_digits # => 0 + * BigDecimal("1").n_significant_digits # => 1 + * BigDecimal("1.1").n_significant_digits # => 2 + * BigDecimal("3.1415").n_significant_digits # => 5 + * BigDecimal("-1e20").n_significant_digits # => 1 + * BigDecimal("1e-20").n_significant_digits # => 1 + * BigDecimal("Infinity").n_significant_digits # => 0 + * BigDecimal("-Infinity").n_significant_digits # => 0 + * BigDecimal("NaN").n_significant_digits # => 0 */ static VALUE BigDecimal_n_significant_digits(VALUE self) From 23eaff3ae51eca11a81c9232b2b253c1ecd21101 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 10:50:33 +0900 Subject: [PATCH 20/33] Rewrite check_rounding_mode function Use table-lookup algorithm instead of consecutive if-statements. --- ext/bigdecimal/bigdecimal.c | 79 ++++++++++++++++++++----------------- ext/bigdecimal/bigdecimal.h | 37 ++++++++++++----- 2 files changed, 70 insertions(+), 46 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 20158c27..40a825df 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -61,6 +61,13 @@ static ID id_to_r; static ID id_eq; static ID id_half; +#define RBD_NUM_ROUNDING_MODES 11 + +static struct { + ID id; + uint8_t mode; +} rbd_rounding_modes[RBD_NUM_ROUNDING_MODES]; + /* MACRO's to guard objects from GC by keeping them in stack */ #ifdef RBIMPL_ATTR_MAYBE_UNUSED #define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0 @@ -667,34 +674,23 @@ check_rounding_mode(VALUE const v) { unsigned short sw; ID id; - switch (TYPE(v)) { - case T_SYMBOL: - id = SYM2ID(v); - if (id == id_up) - return VP_ROUND_UP; - if (id == id_down || id == id_truncate) - return VP_ROUND_DOWN; - if (id == id_half_up || id == id_default) - return VP_ROUND_HALF_UP; - if (id == id_half_down) - return VP_ROUND_HALF_DOWN; - if (id == id_half_even || id == id_banker) - return VP_ROUND_HALF_EVEN; - if (id == id_ceiling || id == id_ceil) - return VP_ROUND_CEIL; - if (id == id_floor) - return VP_ROUND_FLOOR; - rb_raise(rb_eArgError, "invalid rounding mode"); - - default: - break; + if (RB_TYPE_P(v, T_SYMBOL)) { + int i; + id = SYM2ID(v); + for (i = 0; i < RBD_NUM_ROUNDING_MODES; ++i) { + if (rbd_rounding_modes[i].id == id) { + return rbd_rounding_modes[i].mode; + } + } + rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v); } - - sw = NUM2USHORT(v); - if (!VpIsRoundMode(sw)) { - rb_raise(rb_eArgError, "invalid rounding mode"); + else { + sw = NUM2USHORT(v); + if (!VpIsRoundMode(sw)) { + rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v); + } + return sw; } - return sw; } /* call-seq: @@ -4419,17 +4415,26 @@ Init_bigdecimal(void) rb_define_singleton_method(rb_mBigMath, "exp", BigMath_s_exp, 2); rb_define_singleton_method(rb_mBigMath, "log", BigMath_s_log, 2); - id_up = rb_intern_const("up"); - id_down = rb_intern_const("down"); - id_truncate = rb_intern_const("truncate"); - id_half_up = rb_intern_const("half_up"); - id_default = rb_intern_const("default"); - id_half_down = rb_intern_const("half_down"); - id_half_even = rb_intern_const("half_even"); - id_banker = rb_intern_const("banker"); - id_ceiling = rb_intern_const("ceiling"); - id_ceil = rb_intern_const("ceil"); - id_floor = rb_intern_const("floor"); +#define ROUNDING_MODE(i, name, value) \ + id_##name = rb_intern_const(#name); \ + rbd_rounding_modes[i].id = id_##name; \ + rbd_rounding_modes[i].mode = value; + + ROUNDING_MODE(0, up, RBD_ROUND_UP); + ROUNDING_MODE(1, down, RBD_ROUND_DOWN); + ROUNDING_MODE(2, half_up, RBD_ROUND_HALF_UP); + ROUNDING_MODE(3, half_down, RBD_ROUND_HALF_DOWN); + ROUNDING_MODE(4, ceil, RBD_ROUND_CEIL); + ROUNDING_MODE(5, floor, RBD_ROUND_FLOOR); + ROUNDING_MODE(6, half_even, RBD_ROUND_HALF_EVEN); + + ROUNDING_MODE(7, default, RBD_ROUND_DEFAULT); + ROUNDING_MODE(8, truncate, RBD_ROUND_TRUNCATE); + ROUNDING_MODE(9, banker, RBD_ROUND_BANKER); + ROUNDING_MODE(10, ceiling, RBD_ROUND_CEILING); + +#undef ROUNDING_MODE + id_to_r = rb_intern_const("to_r"); id_eq = rb_intern_const("=="); id_half = rb_intern_const("half"); diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index bd1c4674..38dee51e 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -102,7 +102,7 @@ extern VALUE rb_cBigDecimal; */ #define VP_EXPORT static -/* Exception codes */ +/* Exception mode */ #define VP_EXCEPTION_ALL ((unsigned short)0x00FF) #define VP_EXCEPTION_INFINITY ((unsigned short)0x0001) #define VP_EXCEPTION_NaN ((unsigned short)0x0002) @@ -115,18 +115,36 @@ extern VALUE rb_cBigDecimal; #define BIGDECIMAL_EXCEPTION_MODE_DEFAULT 0U -/* Computation mode */ +/* This is used in BigDecimal#mode */ #define VP_ROUND_MODE ((unsigned short)0x0100) -#define VP_ROUND_UP 1 -#define VP_ROUND_DOWN 2 -#define VP_ROUND_HALF_UP 3 -#define VP_ROUND_HALF_DOWN 4 -#define VP_ROUND_CEIL 5 -#define VP_ROUND_FLOOR 6 -#define VP_ROUND_HALF_EVEN 7 + +/* Rounding mode */ +#define VP_ROUND_UP RBD_ROUND_UP +#define VP_ROUND_DOWN RBD_ROUND_DOWN +#define VP_ROUND_HALF_UP RBD_ROUND_HALF_UP +#define VP_ROUND_HALF_DOWN RBD_ROUND_HALF_DOWN +#define VP_ROUND_CEIL RBD_ROUND_CEIL +#define VP_ROUND_FLOOR RBD_ROUND_FLOOR +#define VP_ROUND_HALF_EVEN RBD_ROUND_HALF_EVEN + +enum rbd_rounding_mode { + RBD_ROUND_UP = 1, + RBD_ROUND_DOWN = 2, + RBD_ROUND_HALF_UP = 3, + RBD_ROUND_HALF_DOWN = 4, + RBD_ROUND_CEIL = 5, + RBD_ROUND_FLOOR = 6, + RBD_ROUND_HALF_EVEN = 7, + + RBD_ROUND_DEFAULT = RBD_ROUND_HALF_UP, + RBD_ROUND_TRUNCATE = RBD_ROUND_DOWN, + RBD_ROUND_BANKER = RBD_ROUND_HALF_EVEN, + RBD_ROUND_CEILING = RBD_ROUND_CEIL +}; #define BIGDECIMAL_ROUNDING_MODE_DEFAULT VP_ROUND_HALF_UP +/* Sign flag */ #define VP_SIGN_NaN 0 /* NaN */ #define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */ #define VP_SIGN_NEGATIVE_ZERO -1 /* Negative zero */ @@ -135,6 +153,7 @@ extern VALUE rb_cBigDecimal; #define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */ #define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */ +/* The size of fraction part array */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define FLEXIBLE_ARRAY_SIZE /* */ #elif defined(__GNUC__) && !defined(__STRICT_ANSI__) From e1c6c9be25b41436bb1ac49a8cf7c566656624be Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 11:01:09 +0900 Subject: [PATCH 21/33] Tweak check_rounding_mode_option --- ext/bigdecimal/bigdecimal.c | 19 +++++++++---------- test/bigdecimal/test_bigdecimal.rb | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 40a825df..5ae0b96a 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -630,18 +630,19 @@ check_rounding_mode_option(VALUE const opts) assert(RB_TYPE_P(opts, T_HASH)); if (NIL_P(opts)) - goto noopt; + goto no_opt; mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef); if (mode == Qundef || NIL_P(mode)) - goto noopt; + goto no_opt; if (SYMBOL_P(mode)) mode = rb_sym2str(mode); else if (!RB_TYPE_P(mode, T_STRING)) { - VALUE str_mode = rb_check_string_type(mode); - if (NIL_P(str_mode)) goto invalid; - mode = str_mode; + VALUE str_mode = rb_check_string_type(mode); + if (NIL_P(str_mode)) + goto invalid; + mode = str_mode; } s = RSTRING_PTR(mode); l = RSTRING_LEN(mode); @@ -659,13 +660,11 @@ check_rounding_mode_option(VALUE const opts) default: break; } + invalid: - if (NIL_P(mode)) - rb_raise(rb_eArgError, "invalid rounding mode: nil"); - else - rb_raise(rb_eArgError, "invalid rounding mode: %"PRIsVALUE, mode); + rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", mode); - noopt: + no_opt: return VpGetRoundMode(); } diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 0cd85249..02282405 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1370,8 +1370,18 @@ def test_round_half_nil end def test_round_half_invalid_option - assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('12.5').round(half: :invalid) } - assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('2.15').round(1, half: :invalid) } + assert_raise_with_message(ArgumentError, "invalid rounding mode (upp)") do + BigDecimal('12.5').round(half: :upp) + end + assert_raise_with_message(ArgumentError, "invalid rounding mode (evenn)") do + BigDecimal('2.15').round(1, half: :evenn) + end + assert_raise_with_message(ArgumentError, "invalid rounding mode (downn)") do + BigDecimal('2.15').round(1, half: :downn) + end + assert_raise_with_message(ArgumentError, "invalid rounding mode (42)") do + BigDecimal('2.15').round(1, half: 42) + end end def test_truncate From 69d0588a3b6e10262416830337f7d53ecd4358de Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 11:46:00 +0900 Subject: [PATCH 22/33] Twak GetPrecisionInt and rename it to check_int_precision --- ext/bigdecimal/bigdecimal.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 5ae0b96a..0b424f62 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -928,11 +928,17 @@ GetAddSubPrec(Real *a, Real *b) return mx; } -static SIGNED_VALUE -GetPrecisionInt(VALUE v) +static inline SIGNED_VALUE +check_int_precision(VALUE v) { SIGNED_VALUE n; - n = NUM2INT(v); +#if SIZEOF_VALUE <= SIZEOF_LONG + n = (SIGNED_VALUE)NUM2LONG(v); +#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG + n = (SIGNED_VALUE)NUM2LL(v); +#else +# error SIZEOF_VALUE is too large +#endif if (n < 0) { rb_raise(rb_eArgError, "negative precision"); } @@ -1716,7 +1722,7 @@ BigDecimal_quo(int argc, VALUE *argv, VALUE self) argc = rb_scan_args(argc, argv, "11", &value, &digits); if (argc > 1) { - n = GetPrecisionInt(digits); + n = check_int_precision(digits); } if (n > 0) { @@ -1981,7 +1987,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n) } /* div in BigDecimal sense */ - ix = GetPrecisionInt(n); + ix = check_int_precision(n); if (ix == 0) { return BigDecimal_div(self, b); } @@ -2086,7 +2092,7 @@ BigDecimal_add2(VALUE self, VALUE b, VALUE n) { ENTER(2); Real *cv; - SIGNED_VALUE mx = GetPrecisionInt(n); + SIGNED_VALUE mx = check_int_precision(n); if (mx == 0) return BigDecimal_add(self, b); else { size_t pl = VpSetPrecLimit(0); @@ -2116,7 +2122,7 @@ BigDecimal_sub2(VALUE self, VALUE b, VALUE n) { ENTER(2); Real *cv; - SIGNED_VALUE mx = GetPrecisionInt(n); + SIGNED_VALUE mx = check_int_precision(n); if (mx == 0) return BigDecimal_sub(self, b); else { size_t pl = VpSetPrecLimit(0); @@ -2159,7 +2165,7 @@ BigDecimal_mult2(VALUE self, VALUE b, VALUE n) { ENTER(2); Real *cv; - SIGNED_VALUE mx = GetPrecisionInt(n); + SIGNED_VALUE mx = check_int_precision(n); if (mx == 0) return BigDecimal_mult(self, b); else { size_t pl = VpSetPrecLimit(0); @@ -2214,7 +2220,8 @@ BigDecimal_sqrt(VALUE self, VALUE nFig) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - n = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG; + n = check_int_precision(nFig); + n += VpDblFig() + VpBaseFig(); if (mx <= n) mx = n; GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); VpSqrt(c, a); From a5ab34a1159a53555381a7869791efc03e41851c Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 12:46:22 +0900 Subject: [PATCH 23/33] Rewrite allocation functions * Rename them * Make allocation count operations atomic --- ext/bigdecimal/bigdecimal.c | 159 ++++++++++++++++++++---------------- ext/bigdecimal/bigdecimal.h | 3 - 2 files changed, 90 insertions(+), 72 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 0b424f62..fcb4f460 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -112,6 +112,77 @@ static struct { #define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0) #define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0) +/* + * ================== Memory allocation ============================ + */ + +#ifdef BIGDECIMAL_DEBUG +static size_t rbd_allocation_count = 0; /* Memory allocation counter */ +static inline void +atomic_allocation_count_inc(void) +{ + RUBY_ATOMIC_SIZE_INC(rbd_allocation_count); +} +static inline void +atomic_allocation_count_dec_nounderflow(void) +{ + if (rbd_allocation_count == 0) return; + RUBY_ATOMIC_SIZE_DEC(rbd_allocation_count); +} +static void +check_allocation_count_nonzero(void) +{ + if (rbd_allocation_count != 0) return; + rb_bug("[bigdecimal][rbd_free_struct] Too many memory free calls"); +} +#else +# define atomic_allocation_count_inc() /* nothing */ +# define atomic_allocation_count_dec_nounderflow() /* nothing */ +# define check_allocation_count_nonzero() /* nothing */ +#endif /* BIGDECIMAL_DEBUG */ + +PUREFUNC(static inline size_t rbd_struct_size(size_t const)); + +static inline size_t +rbd_struct_size(size_t const internal_digits) +{ + return offsetof(Real, frac) + sizeof(DECDIG) * internal_digits; +} + +static inline Real * +rbd_allocate_struct(size_t const internal_digits) +{ + size_t const size = rbd_struct_size(internal_digits); + Real *real = ruby_xcalloc(1, size); + atomic_allocation_count_inc(); + return real; +} + +static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp); + +static inline Real * +rbd_reallocate_struct(Real *real, size_t const internal_digits) +{ + size_t const size = rbd_struct_size(internal_digits); + VALUE obj = real ? real->obj : 0; + Real *new_real = (Real *)ruby_xrealloc(real, size); + if (obj) { + new_real->obj = 0; + BigDecimal_wrap_struct(obj, new_real); + } + return new_real; +} + +static void +rbd_free_struct(Real *real) +{ + if (real != NULL) { + check_allocation_count_nonzero(); + ruby_xfree(real); + atomic_allocation_count_dec_nounderflow(); + } +} + /* * ================== Ruby Interface part ========================== */ @@ -145,7 +216,7 @@ static VALUE BigDecimal_negative_zero(void); static void BigDecimal_delete(void *pv) { - VpFree(pv); + rbd_free_struct(pv); } static size_t @@ -980,26 +1051,12 @@ VpCreateRbObject(size_t mx, const char *str, bool raise_exception) return VpNewRbClass(mx, str, rb_cBigDecimal, true, raise_exception); } -#define VpAllocReal(prec) (Real *)VpMemAlloc(offsetof(Real, frac) + (prec) * sizeof(DECDIG)) - -static Real * -VpReallocReal(Real *pv, size_t prec) -{ - VALUE obj = pv ? pv->obj : 0; - Real *new_pv = (Real *)VpMemRealloc(pv, offsetof(Real, frac) + prec * sizeof(DECDIG)); - if (obj) { - new_pv->obj = 0; - BigDecimal_wrap_struct(obj, new_pv); - } - return new_pv; -} - static Real * VpCopy(Real *pv, Real const* const x) { assert(x != NULL); - pv = VpReallocReal(pv, x->MaxPrec); + pv = rbd_reallocate_struct(pv, x->MaxPrec); pv->MaxPrec = x->MaxPrec; pv->Prec = x->Prec; pv->exponent = x->exponent; @@ -1825,7 +1882,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) if (!VpIsZero(c) && (VpGetSign(a) * VpGetSign(b) < 0)) { /* result adjustment for negative case */ - res = VpReallocReal(res, d->MaxPrec); + res = rbd_reallocate_struct(res, d->MaxPrec); res->MaxPrec = d->MaxPrec; VpAddSub(res, d, VpOne(), -1); GUARD_OBJ(d, VpCreateRbObject(GetAddSubPrec(c, b) * 2*BASE_FIG, "0", true)); @@ -3116,7 +3173,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r Real *vp; if (uval == 0) { - vp = VpAllocReal(1); + vp = rbd_allocate_struct(1); vp->MaxPrec = 1; vp->Prec = 1; vp->exponent = 1; @@ -3124,7 +3181,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r vp->frac[0] = 0; } else if (uval < BASE) { - vp = VpAllocReal(1); + vp = rbd_allocate_struct(1); vp->MaxPrec = 1; vp->Prec = 1; vp->exponent = 1; @@ -3150,7 +3207,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval, RB_UNUSED_VAR(size_t digs), int r } const size_t exp = len + ntz; - vp = VpAllocReal(len); + vp = rbd_allocate_struct(len); vp->MaxPrec = len; vp->Prec = len; vp->exponent = exp; @@ -4494,42 +4551,6 @@ static int VpRdup(Real *m, size_t ind_m); static int gnAlloc = 0; /* Memory allocation counter */ #endif /* BIGDECIMAL_DEBUG */ -VP_EXPORT void * -VpMemAlloc(size_t mb) -{ - void *p = xmalloc(mb); - memset(p, 0, mb); -#ifdef BIGDECIMAL_DEBUG - gnAlloc++; /* Count allocation call */ -#endif /* BIGDECIMAL_DEBUG */ - return p; -} - -VP_EXPORT void * -VpMemRealloc(void *ptr, size_t mb) -{ - return xrealloc(ptr, mb); -} - -VP_EXPORT void -VpFree(Real *pv) -{ - if (pv != NULL) { - xfree(pv); -#ifdef BIGDECIMAL_DEBUG - gnAlloc--; /* Decrement allocation count */ - if (gnAlloc == 0) { - printf(" *************** All memories allocated freed ****************\n"); - /*getchar();*/ - } - if (gnAlloc < 0) { - printf(" ??????????? Too many memory free calls(%d) ?????????????\n", gnAlloc); - /*getchar();*/ - } -#endif /* BIGDECIMAL_DEBUG */ - } -} - /* * EXCEPTION Handling. */ @@ -5009,7 +5030,7 @@ bigdecimal_parse_special_string(const char *str) p = str + table[i].len; while (*p && ISSPACE(*p)) ++p; if (*p == '\0') { - Real *vp = VpAllocReal(1); + Real *vp = rbd_allocate_struct(1); vp->MaxPrec = 1; switch (table[i].sign) { default: @@ -5079,7 +5100,7 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) /* necessary to be able to store */ /* at least mx digits. */ /* szVal==NULL ==> allocate zero value. */ - vp = VpAllocReal(mx); + vp = rbd_allocate_struct(mx); vp->MaxPrec = mx; /* set max precision */ VpSetZero(vp, 1); /* initialize vp to zero. */ return vp; @@ -5254,7 +5275,7 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) if (mx == 0) mx = 1; nalloc = Max(nalloc, mx); mx = nalloc; - vp = VpAllocReal(mx); + vp = rbd_allocate_struct(mx); vp->MaxPrec = mx; /* set max precision */ VpSetZero(vp, sign); VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne); @@ -5828,8 +5849,8 @@ VpMult(Real *c, Real *a, Real *b) c->exponent = a->exponent; /* set exponent */ if (!AddExponent(c, b->exponent)) { - if (w) VpFree(c); - return 0; + if (w) rbd_free_struct(c); + return 0; } VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */ carry = 0; @@ -5879,10 +5900,10 @@ VpMult(Real *c, Real *a, Real *b) } } if (w != NULL) { /* free work variable */ - VpNmlz(c); - VpAsgn(w, c, 1); - VpFree(c); - c = w; + VpNmlz(c); + VpAsgn(w, c, 1); + rbd_free_struct(c); + c = w; } else { VpLimitRound(c,0); @@ -7047,8 +7068,8 @@ VpSqrt(Real *y, Real *x) y->MaxPrec = y_prec; Exit: - VpFree(f); - VpFree(r); + rbd_free_struct(f); + rbd_free_struct(r); return 1; } @@ -7470,8 +7491,8 @@ VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n) printf(" n=%"PRIdVALUE"\n", n); } #endif /* BIGDECIMAL_DEBUG */ - VpFree(w2); - VpFree(w1); + rbd_free_struct(w2); + rbd_free_struct(w1); return 1; } diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index 38dee51e..46fafde1 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -224,9 +224,6 @@ VP_EXPORT int VpIsNegDoubleZero(double v); #endif VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt); VP_EXPORT size_t VpInit(DECDIG BaseVal); -VP_EXPORT void *VpMemAlloc(size_t mb); -VP_EXPORT void *VpMemRealloc(void *ptr, size_t mb); -VP_EXPORT void VpFree(Real *pv); VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal, int strict_p, int exc); VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw); VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation); From 14f3d965f8dfe1c92d8ea7704f177b38747a90bf Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 13:26:50 +0900 Subject: [PATCH 24/33] Tweak VpAlloc * Stop reusing mx and mf * Check szVal == NULL first * Treat special values before checking the leading `#` --- ext/bigdecimal/bigdecimal.c | 64 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index fcb4f460..4e4ac991 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -5054,11 +5054,11 @@ bigdecimal_parse_special_string(const char *str) /* * Allocates variable. * [Input] - * mx ... allocation unit, if zero then mx is determined by szVal. - * The mx is the number of effective digits can to be stored. - * szVal ... value assigned(char). If szVal==NULL,then zero is assumed. - * If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7), - * full precision specified by szVal is allocated. + * mx ... The number of decimal digits to be allocated, if zero then mx is determined by szVal. + * The mx will be the number of significant digits can to be stored. + * szVal ... The value assigned(char). If szVal==NULL, then zero is assumed. + * If szVal[0]=='#' then MaxPrec is not affected by the precision limit + * so that the full precision specified by szVal is allocated. * * [Returns] * Pointer to the newly allocated variable, or @@ -5069,48 +5069,48 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) { const char *orig_szVal = szVal; size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc; + size_t len; char v, *psz; int sign=1; Real *vp = NULL; - size_t mf = VpGetPrecLimit(); + size_t prec_limit = VpGetPrecLimit(); VALUE buf; - mx = (mx + BASE_FIG - 1) / BASE_FIG; /* Determine allocation unit. */ - if (mx == 0) ++mx; + len = (mx + BASE_FIG - 1) / BASE_FIG; /* Determine allocation unit. */ + if (len == 0) ++len; - if (szVal) { - /* Skipping leading spaces */ - while (ISSPACE(*szVal)) szVal++; - - /* Processing the leading one `#` */ - if (*szVal != '#') { - if (mf) { - mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */ - if (mx > mf) { - mx = mf; - } - } - } - else { - ++szVal; - } - } - else { + if (szVal == NULL) { return_zero: /* necessary to be able to store */ /* at least mx digits. */ /* szVal==NULL ==> allocate zero value. */ vp = rbd_allocate_struct(mx); - vp->MaxPrec = mx; /* set max precision */ + vp->MaxPrec = len; /* set max precision */ VpSetZero(vp, 1); /* initialize vp to zero. */ return vp; } + /* Skipping leading spaces */ + while (ISSPACE(*szVal)) szVal++; + /* Check on Inf & NaN */ if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) { return vp; } + /* Processing the leading one `#` */ + if (*szVal != '#') { + if (prec_limit) { + size_t const max_len = (prec_limit + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */ + if (len > max_len) { + len = max_len; + } + } + } + else { + ++szVal; + } + /* Scanning digits */ /* A buffer for keeping scanned digits */ @@ -5272,11 +5272,11 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */ /* units for szVal[] */ - if (mx == 0) mx = 1; - nalloc = Max(nalloc, mx); - mx = nalloc; - vp = rbd_allocate_struct(mx); - vp->MaxPrec = mx; /* set max precision */ + if (len == 0) len = 1; + nalloc = Max(nalloc, len); + len = nalloc; + vp = rbd_allocate_struct(len); + vp->MaxPrec = len; /* set max precision */ VpSetZero(vp, sign); VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne); rb_str_resize(buf, 0); From 5391f7e92c0a0f0dc93db0ac910d8ca47f67800c Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 14:59:21 +0900 Subject: [PATCH 25/33] Make VPrint function always available --- ext/bigdecimal/bigdecimal.c | 177 ++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 91 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 4e4ac991..bad10ae8 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -7,9 +7,7 @@ */ /* #define BIGDECIMAL_DEBUG 1 */ -#ifdef BIGDECIMAL_DEBUG -# define BIGDECIMAL_ENABLE_VPRINT 1 -#endif + #include "bigdecimal.h" #include "ruby/util.h" @@ -198,10 +196,7 @@ static VALUE VpCheckGetValue(Real *p); static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v); static int VpLimitRound(Real *c, size_t ixDigit); static Real *VpCopy(Real *pv, Real const* const x); - -#ifdef BIGDECIMAL_ENABLE_VPRINT static int VPrint(FILE *fp,const char *cntl_chr,Real *a); -#endif /* * **** BigDecimal part **** @@ -4501,6 +4496,8 @@ Init_bigdecimal(void) id_to_r = rb_intern_const("to_r"); id_eq = rb_intern_const("=="); id_half = rb_intern_const("half"); + + (void)VPrint; /* suppress unused warning */ } /* @@ -6272,7 +6269,6 @@ VpComp(Real *a, Real *b) * Note: % must not appear more than once * a ... VP variable to be printed */ -#ifdef BIGDECIMAL_ENABLE_VPRINT static int VPrint(FILE *fp, const char *cntl_chr, Real *a) { @@ -6285,95 +6281,94 @@ VPrint(FILE *fp, const char *cntl_chr, Real *a) /* nc : number of characters printed */ ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ while (*(cntl_chr + j)) { - if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') { - nc = 0; - if (VpIsNaN(a)) { - fprintf(fp, SZ_NaN); - nc += 8; - } - else if (VpIsPosInf(a)) { - fprintf(fp, SZ_INF); - nc += 8; - } - else if (VpIsNegInf(a)) { - fprintf(fp, SZ_NINF); - nc += 9; - } - else if (!VpIsZero(a)) { - if (BIGDECIMAL_NEGATIVE_P(a)) { - fprintf(fp, "-"); - ++nc; - } - nc += fprintf(fp, "0."); - switch (*(cntl_chr + j + 1)) { - default: - break; + if (*(cntl_chr + j) == '%' && *(cntl_chr + j + 1) != '%') { + nc = 0; + if (VpIsNaN(a)) { + fprintf(fp, SZ_NaN); + nc += 8; + } + else if (VpIsPosInf(a)) { + fprintf(fp, SZ_INF); + nc += 8; + } + else if (VpIsNegInf(a)) { + fprintf(fp, SZ_NINF); + nc += 9; + } + else if (!VpIsZero(a)) { + if (BIGDECIMAL_NEGATIVE_P(a)) { + fprintf(fp, "-"); + ++nc; + } + nc += fprintf(fp, "0."); + switch (*(cntl_chr + j + 1)) { + default: + break; - case '0': case 'z': - ZeroSup = 0; - ++j; - sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10; - break; - } - for (i = 0; i < a->Prec; ++i) { - m = BASE1; - e = a->frac[i]; - while (m) { - nn = e / m; - if (!ZeroSup || nn) { - nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */ - /* as 0.00xx will not */ - /* be printed. */ - ++nd; - ZeroSup = 0; /* Set to print succeeding zeros */ - } - if (nd >= sep) { /* print ' ' after every 10 digits */ - nd = 0; - nc += fprintf(fp, " "); - } - e = e - nn * m; - m /= 10; - } - } - nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a)); - nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec); - } - else { - nc += fprintf(fp, "0.0"); - } - } - else { - ++nc; - if (*(cntl_chr + j) == '\\') { - switch (*(cntl_chr + j + 1)) { - case 'n': - fprintf(fp, "\n"); - ++j; - break; - case 't': - fprintf(fp, "\t"); - ++j; - break; - case 'b': - fprintf(fp, "\n"); - ++j; - break; - default: - fprintf(fp, "%c", *(cntl_chr + j)); - break; - } - } - else { - fprintf(fp, "%c", *(cntl_chr + j)); - if (*(cntl_chr + j) == '%') ++j; - } - } - j++; + case '0': case 'z': + ZeroSup = 0; + ++j; + sep = cntl_chr[j] == 'z' ? BIGDECIMAL_COMPONENT_FIGURES : 10; + break; + } + for (i = 0; i < a->Prec; ++i) { + m = BASE1; + e = a->frac[i]; + while (m) { + nn = e / m; + if (!ZeroSup || nn) { + nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */ + /* as 0.00xx will not */ + /* be printed. */ + ++nd; + ZeroSup = 0; /* Set to print succeeding zeros */ + } + if (nd >= sep) { /* print ' ' after every 10 digits */ + nd = 0; + nc += fprintf(fp, " "); + } + e = e - nn * m; + m /= 10; + } + } + nc += fprintf(fp, "E%"PRIdSIZE, VpExponent10(a)); + nc += fprintf(fp, " (%"PRIdVALUE", %lu, %lu)", a->exponent, a->Prec, a->MaxPrec); + } + else { + nc += fprintf(fp, "0.0"); + } + } + else { + ++nc; + if (*(cntl_chr + j) == '\\') { + switch (*(cntl_chr + j + 1)) { + case 'n': + fprintf(fp, "\n"); + ++j; + break; + case 't': + fprintf(fp, "\t"); + ++j; + break; + case 'b': + fprintf(fp, "\n"); + ++j; + break; + default: + fprintf(fp, "%c", *(cntl_chr + j)); + break; + } + } + else { + fprintf(fp, "%c", *(cntl_chr + j)); + if (*(cntl_chr + j) == '%') ++j; + } + } + j++; } return (int)nc; } -#endif static void VpFormatSt(char *psz, size_t fFmt) From 40c826f5e6b692458153b4addb41be34dd3f3e27 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 14:59:55 +0900 Subject: [PATCH 26/33] Add and use specific value allocators * Add rbd_allocate_struct_zero for making 0.0 * Add rbd_allocate_struct_one for making 1.0 * Use them to replace VpAlloc calls * Renmae VpPt5 to VpConstPt5 --- ext/bigdecimal/bigdecimal.c | 69 +++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index bad10ae8..a914d995 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -144,7 +144,8 @@ PUREFUNC(static inline size_t rbd_struct_size(size_t const)); static inline size_t rbd_struct_size(size_t const internal_digits) { - return offsetof(Real, frac) + sizeof(DECDIG) * internal_digits; + size_t const frac_len = (internal_digits == 0) ? 1 : internal_digits; + return offsetof(Real, frac) + frac_len * sizeof(DECDIG); } static inline Real * @@ -153,6 +154,7 @@ rbd_allocate_struct(size_t const internal_digits) size_t const size = rbd_struct_size(internal_digits); Real *real = ruby_xcalloc(1, size); atomic_allocation_count_inc(); + real->MaxPrec = internal_digits; return real; } @@ -164,6 +166,7 @@ rbd_reallocate_struct(Real *real, size_t const internal_digits) size_t const size = rbd_struct_size(internal_digits); VALUE obj = real ? real->obj : 0; Real *new_real = (Real *)ruby_xrealloc(real, size); + new_real->MaxPrec = internal_digits; if (obj) { new_real->obj = 0; BigDecimal_wrap_struct(obj, new_real); @@ -181,6 +184,23 @@ rbd_free_struct(Real *real) } } +static Real * +rbd_allocate_struct_zero(size_t const digits, int sign) +{ + size_t const len = roomof(digits, BASE_FIG); + Real *real = rbd_allocate_struct(len); + VpSetZero(real, sign); + return real; +} + +static Real * +rbd_allocate_struct_one(size_t const digits, int sign) +{ + Real *real = rbd_allocate_struct_zero(digits, sign); + VpSetOne(real); + return real; +} + /* * ================== Ruby Interface part ========================== */ @@ -4517,7 +4537,7 @@ static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */ #endif /* BIGDECIMAL_DEBUG */ static Real *VpConstOne; /* constant 1.0 */ -static Real *VpPt5; /* constant 0.5 */ +static Real *VpConstPt5; /* constant 0.5 */ #define maxnr 100UL /* Maximum iterations for calculating sqrt. */ /* used in VpSqrt() */ @@ -4936,9 +4956,13 @@ VpInit(DECDIG BaseVal) /* Setup +/- Inf NaN -0 */ VpGetDoubleNegZero(); - /* Allocates Vp constants. */ - VpConstOne = VpAlloc(1UL, "1", 1, 1); - VpPt5 = VpAlloc(1UL, ".5", 1, 1); + /* Const 1.0 */ + VpConstOne = rbd_allocate_struct_one(1, 1); + + /* Const 0.5 */ + VpConstPt5 = rbd_allocate_struct_one(1, 1); + VpConstPt5->exponent = 0; + VpConstPt5->frac[0] = 5*BASE1; #ifdef BIGDECIMAL_DEBUG gnAlloc = 0; @@ -5838,7 +5862,7 @@ VpMult(Real *c, Real *a, Real *b) if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */ w = c; - c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0", 1, 1); + c = rbd_allocate_struct_zero((size_t)((MxIndAB + 1) * BASE_FIG), 1); MxIndC = MxIndAB; } @@ -7003,8 +7027,9 @@ VpSqrt(Real *y, Real *x) if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec; /* allocate temporally variables */ - f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1", 1, 1); - r = VpAlloc((n + n) * (BASE_FIG + 2), "#1", 1, 1); + /* TODO: reconsider MaxPrec of f and r */ + f = rbd_allocate_struct_one(y->MaxPrec * (BASE_FIG + 2), 1); + r = rbd_allocate_struct_one((n + n) * (BASE_FIG + 2), 1); nr = 0; y_prec = y->MaxPrec; @@ -7029,16 +7054,21 @@ VpSqrt(Real *y, Real *x) f->MaxPrec = y->MaxPrec + 1; n = (SIGNED_VALUE)(y_prec * BASE_FIG); if (n < (SIGNED_VALUE)maxnr) n = (SIGNED_VALUE)maxnr; + + /* + * Perform: y_{n+1} = (y_n - x/y_n) / 2 + */ do { - y->MaxPrec *= 2; - if (y->MaxPrec > y_prec) y->MaxPrec = y_prec; - f->MaxPrec = y->MaxPrec; - VpDivd(f, r, x, y); /* f = x/y */ - VpAddSub(r, f, y, -1); /* r = f - y */ - VpMult(f, VpPt5, r); /* f = 0.5*r */ - if (VpIsZero(f)) goto converge; - VpAddSub(r, f, y, 1); /* r = y + f */ - VpAsgn(y, r, 1); /* y = r */ + y->MaxPrec *= 2; + if (y->MaxPrec > y_prec) y->MaxPrec = y_prec; + f->MaxPrec = y->MaxPrec; + VpDivd(f, r, x, y); /* f = x/y */ + VpAddSub(r, f, y, -1); /* r = f - y */ + VpMult(f, VpConstPt5, r); /* f = 0.5*r */ + if (VpIsZero(f)) + goto converge; + VpAddSub(r, f, y, 1); /* r = y + f */ + VpAsgn(y, r, 1); /* y = r */ } while (++nr < n); #ifdef BIGDECIMAL_DEBUG @@ -7455,9 +7485,10 @@ VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n) } /* Allocate working variables */ + /* TODO: reconsider MaxPrec of w1 and w2 */ + w1 = rbd_allocate_struct_zero((y->MaxPrec + 2) * BASE_FIG, 1); + w2 = rbd_allocate_struct_zero((w1->MaxPrec * 2 + 1) * BASE_FIG, 1); - w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0", 1, 1); - w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0", 1, 1); /* calculation start */ VpAsgn(y, x, 1); From 9276a94ac79ccb4ad6899bc65a3c2527c642ece6 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 13 Nov 2022 21:05:46 +0900 Subject: [PATCH 27/33] Add specific value allocators * Add NewZero* and NewOne* function families * Use them instead of VpAlloc for allocating 0 and 1 --- ext/bigdecimal/bigdecimal.c | 481 +++++++++++++++++++++--------------- 1 file changed, 282 insertions(+), 199 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index a914d995..a5b80464 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -158,9 +158,33 @@ rbd_allocate_struct(size_t const internal_digits) return real; } -static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp); +static size_t +rbd_calculate_internal_digits(size_t const digits, bool limit_precision) +{ + size_t const len = roomof(digits, BASE_FIG); + if (limit_precision) { + size_t const prec_limit = VpGetPrecLimit(); + if (prec_limit > 0) { + /* NOTE: 2 more digits for rounding and division */ + size_t const max_len = roomof(prec_limit, BASE_FIG) + 2; + if (len > max_len) + return max_len; + } + } + + return len; +} static inline Real * +rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_precision) +{ + size_t const internal_digits = rbd_calculate_internal_digits(decimal_digits, limit_precision); + return rbd_allocate_struct(internal_digits); +} + +static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp); + +static Real * rbd_reallocate_struct(Real *real, size_t const internal_digits) { size_t const size = rbd_struct_size(internal_digits); @@ -184,23 +208,54 @@ rbd_free_struct(Real *real) } } +#define NewZero rbd_allocate_struct_zero static Real * -rbd_allocate_struct_zero(size_t const digits, int sign) +rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision) { - size_t const len = roomof(digits, BASE_FIG); - Real *real = rbd_allocate_struct(len); + Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision); VpSetZero(real, sign); return real; } +#define NewZeroLimited rbd_allocate_struct_zero_limited +static inline Real * +rbd_allocate_struct_zero_limited(int sign, size_t const digits) +{ + return rbd_allocate_struct_zero(sign, digits, true); +} + +#define NewZeroNolimit rbd_allocate_struct_zero_nolimit +static inline Real * +rbd_allocate_struct_zero_nolimit(int sign, size_t const digits) +{ + return rbd_allocate_struct_zero(sign, digits, false); +} + +#define NewOne rbd_allocate_struct_one static Real * -rbd_allocate_struct_one(size_t const digits, int sign) +rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision) { - Real *real = rbd_allocate_struct_zero(digits, sign); + Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision); VpSetOne(real); + if (sign < 0) + VpSetSign(real, VP_SIGN_NEGATIVE_FINITE); return real; } +#define NewOneLimited rbd_allocate_struct_one_limited +static inline Real * +rbd_allocate_struct_one_limited(int sign, size_t const digits) +{ + return rbd_allocate_struct_one(sign, digits, true); +} + +#define NewOneNolimit rbd_allocate_struct_one_nolimit +static inline Real * +rbd_allocate_struct_one_nolimit(int sign, size_t const digits) +{ + return rbd_allocate_struct_one(sign, digits, false); +} + /* * ================== Ruby Interface part ========================== */ @@ -254,6 +309,56 @@ static const rb_data_type_t BigDecimal_data_type = { #endif }; +static Real * +rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision) +{ + Real *real = rbd_allocate_struct_zero(sign, digits, limit_precision); + if (real != NULL) { + VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0); + BigDecimal_wrap_struct(obj, real); + } + return real; +} + +#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap +static inline Real * +rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits) +{ + return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true); +} + +#define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap +static inline Real * +rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits) +{ + return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, false); +} + +static Real * +rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision) +{ + Real *real = rbd_allocate_struct_one(sign, digits, limit_precision); + if (real != NULL) { + VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0); + BigDecimal_wrap_struct(obj, real); + } + return real; +} + +#define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap +static inline Real * +rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits) +{ + return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true); +} + +#define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap +static inline Real * +rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits) +{ + return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, false); +} + static inline int is_kind_of_BigDecimal(VALUE const v) { @@ -1349,17 +1454,17 @@ BigDecimal_add(VALUE self, VALUE r) mx = GetAddSubPrec(a, b); if (mx == (size_t)-1L) { - GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true)); - VpAddSub(c, a, b, 1); + GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1)); + VpAddSub(c, a, b, 1); } else { - GUARD_OBJ(c, VpCreateRbObject(mx * (VpBaseFig() + 1), "0", true)); - if(!mx) { - VpSetInf(c, VpGetSign(a)); - } - else { - VpAddSub(c, a, b, 1); - } + GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1))); + if (!mx) { + VpSetInf(c, VpGetSign(a)); + } + else { + VpAddSub(c, a, b, 1); + } } return VpCheckGetValue(c); } @@ -1404,17 +1509,17 @@ BigDecimal_sub(VALUE self, VALUE r) mx = GetAddSubPrec(a,b); if (mx == (size_t)-1L) { - GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true)); - VpAddSub(c, a, b, -1); + GUARD_OBJ(c, NewZeroWrapLimited(1, VpBaseFig() + 1)); + VpAddSub(c, a, b, -1); } else { - GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true)); - if (!mx) { - VpSetInf(c,VpGetSign(a)); - } - else { - VpAddSub(c, a, b, -1); - } + GUARD_OBJ(c, NewZeroWrapLimited(1, mx *(VpBaseFig() + 1))); + if (!mx) { + VpSetInf(c,VpGetSign(a)); + } + else { + VpAddSub(c, a, b, -1); + } } return VpCheckGetValue(c); } @@ -1654,7 +1759,7 @@ BigDecimal_neg(VALUE self) ENTER(5); Real *c, *a; GUARD_OBJ(a, GetVpValue(self, 1)); - GUARD_OBJ(c, VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, a->Prec *(VpBaseFig() + 1))); VpAsgn(c, a, -1); return VpCheckGetValue(c); } @@ -1681,7 +1786,7 @@ BigDecimal_mult(VALUE self, VALUE r) SAVE(b); mx = a->Prec + b->Prec; - GUARD_OBJ(c, VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx * (VpBaseFig() + 1))); VpMult(c, a, b); return VpCheckGetValue(c); } @@ -1728,8 +1833,8 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div) if (2*BIGDECIMAL_DOUBLE_FIGURES > mx) mx = 2*BIGDECIMAL_DOUBLE_FIGURES; - GUARD_OBJ((*c), VpCreateRbObject(mx + 2*BASE_FIG, "#0", true)); - GUARD_OBJ((*res), VpCreateRbObject((mx + 1)*2 + 2*BASE_FIG, "#0", true)); + GUARD_OBJ((*c), NewZeroWrapNolimit(1, mx + 2*BASE_FIG)); + GUARD_OBJ((*res), NewZeroWrapNolimit(1, (mx + 1)*2 + 2*BASE_FIG)); VpDivd(*c, *res, a, b); return Qnil; @@ -1884,12 +1989,12 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) if (2*BIGDECIMAL_DOUBLE_FIGURES > mx) mx = 2*BIGDECIMAL_DOUBLE_FIGURES; - GUARD_OBJ(c, VpCreateRbObject(mx + 2*BASE_FIG, "0", true)); - GUARD_OBJ(res, VpCreateRbObject(mx*2 + 2*BASE_FIG, "#0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx + 2*BASE_FIG)); + GUARD_OBJ(res, NewZeroWrapNolimit(1, mx*2 + 2*BASE_FIG)); VpDivd(c, res, a, b); mx = c->Prec * BASE_FIG; - GUARD_OBJ(d, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(d, NewZeroWrapLimited(1, mx)); VpActiveRound(d, c, VP_ROUND_DOWN, 0); VpMult(res, d, b); @@ -1900,7 +2005,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) res = rbd_reallocate_struct(res, d->MaxPrec); res->MaxPrec = d->MaxPrec; VpAddSub(res, d, VpOne(), -1); - GUARD_OBJ(d, VpCreateRbObject(GetAddSubPrec(c, b) * 2*BASE_FIG, "0", true)); + GUARD_OBJ(d, NewZeroWrapLimited(1, GetAddSubPrec(c, b) * 2*BASE_FIG)); VpAddSub(d, c, b, 1); *div = res; *mod = d; @@ -1964,17 +2069,17 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv) SAVE(b); mx = (a->MaxPrec + b->MaxPrec) *VpBaseFig(); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); - GUARD_OBJ(res, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true)); - GUARD_OBJ(rr, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true)); - GUARD_OBJ(ff, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); + GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1))); + GUARD_OBJ(rr, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1))); + GUARD_OBJ(ff, NewZeroWrapNolimit(1, (mx+1) * 2 + (VpBaseFig() + 1))); VpDivd(c, res, a, b); mx = c->Prec *(VpBaseFig() + 1); - GUARD_OBJ(d, VpCreateRbObject(mx, "0", true)); - GUARD_OBJ(f, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(d, NewZeroWrapLimited(1, mx)); + GUARD_OBJ(f, NewZeroWrapLimited(1, mx)); VpActiveRound(d, c, VP_ROUND_DOWN, 0); /* 0: round off */ @@ -2070,7 +2175,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n) size_t b_prec = ix; size_t pl = VpSetPrecLimit(0); - GUARD_OBJ(cv, VpCreateRbObject(mx + VpBaseFig(), "0", true)); + GUARD_OBJ(cv, NewZeroWrapLimited(1, mx + VpBaseFig())); GUARD_OBJ(av, GetVpValue(self, 1)); /* TODO: I want to refactor this precision control for a float value later * by introducing an implicit conversion function instead of @@ -2081,7 +2186,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n) GUARD_OBJ(bv, GetVpValueWithPrec(b, b_prec, 1)); mx = av->Prec + bv->Prec + 2; if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1; - GUARD_OBJ(res, VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0", true)); + GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx * 2 + 2)*VpBaseFig())); VpDivd(cv, res, av, bv); VpSetPrecLimit(pl); VpLeftRound(cv, VpGetRoundMode(), ix); @@ -2269,7 +2374,7 @@ BigDecimal_abs(VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec *(VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpAsgn(c, a, 1); VpChangeSign(c, 1); return VpCheckGetValue(c); @@ -2295,7 +2400,7 @@ BigDecimal_sqrt(VALUE self, VALUE nFig) n = check_int_precision(nFig); n += VpDblFig() + VpBaseFig(); if (mx <= n) mx = n; - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpSqrt(c, a); return VpCheckGetValue(c); } @@ -2311,7 +2416,7 @@ BigDecimal_fix(VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec *(VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */ return VpCheckGetValue(c); } @@ -2384,7 +2489,7 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self) pl = VpSetPrecLimit(0); GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpSetPrecLimit(pl); VpActiveRound(c, a, sw, iLoc); if (round_to_int) { @@ -2430,7 +2535,7 @@ BigDecimal_truncate(int argc, VALUE *argv, VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpSetPrecLimit(pl); VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */ if (argc == 0) { @@ -2450,7 +2555,7 @@ BigDecimal_frac(VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpFrac(c, a); return VpCheckGetValue(c); } @@ -2490,7 +2595,7 @@ BigDecimal_floor(int argc, VALUE *argv, VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpSetPrecLimit(pl); VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc); #ifdef BIGDECIMAL_DEBUG @@ -2536,7 +2641,7 @@ BigDecimal_ceil(int argc, VALUE *argv, VALUE self) GUARD_OBJ(a, GetVpValue(self, 1)); mx = a->Prec * (VpBaseFig() + 1); - GUARD_OBJ(c, VpCreateRbObject(mx, "0", true)); + GUARD_OBJ(c, NewZeroWrapLimited(1, mx)); VpSetPrecLimit(pl); VpActiveRound(c, a, VP_ROUND_CEIL, iLoc); if (argc == 0) { @@ -2844,7 +2949,7 @@ bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n) volatile VALUE obj = exp->obj; if (VpIsZero(exp)) { - return VpCheckGetValue(VpCreateRbObject(n, "1", true)); + return VpCheckGetValue(NewOneWrapLimited(1, n)); } log_x = BigMath_log(x->obj, SSIZET2NUM(n+1)); @@ -2882,9 +2987,9 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec); if (VpIsNaN(x)) { - y = VpCreateRbObject(n, "0", true); - RB_GC_GUARD(y->obj); - VpSetNaN(y); + y = NewZeroWrapLimited(1, n); + VpSetNaN(y); + RB_GC_GUARD(y->obj); return VpCheckGetValue(y); } @@ -2953,136 +3058,126 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } if (VpIsZero(x)) { - if (is_negative(vexp)) { - y = VpCreateRbObject(n, "#0", true); - RB_GC_GUARD(y->obj); - if (BIGDECIMAL_NEGATIVE_P(x)) { - if (is_integer(vexp)) { - if (is_even(vexp)) { - /* (-0) ** (-even_integer) -> Infinity */ - VpSetPosInf(y); - } - else { - /* (-0) ** (-odd_integer) -> -Infinity */ - VpSetNegInf(y); - } - } - else { - /* (-0) ** (-non_integer) -> Infinity */ - VpSetPosInf(y); - } - } - else { - /* (+0) ** (-num) -> Infinity */ - VpSetPosInf(y); - } + if (is_negative(vexp)) { + y = NewZeroWrapNolimit(1, n); + if (BIGDECIMAL_NEGATIVE_P(x)) { + if (is_integer(vexp)) { + if (is_even(vexp)) { + /* (-0) ** (-even_integer) -> Infinity */ + VpSetPosInf(y); + } + else { + /* (-0) ** (-odd_integer) -> -Infinity */ + VpSetNegInf(y); + } + } + else { + /* (-0) ** (-non_integer) -> Infinity */ + VpSetPosInf(y); + } + } + else { + /* (+0) ** (-num) -> Infinity */ + VpSetPosInf(y); + } + RB_GC_GUARD(y->obj); return VpCheckGetValue(y); - } - else if (is_zero(vexp)) { - return VpCheckGetValue(VpCreateRbObject(n, "1", true)); - } - else { - return VpCheckGetValue(VpCreateRbObject(n, "0", true)); - } + } + else if (is_zero(vexp)) { + return VpCheckGetValue(NewOneWrapLimited(1, n)); + } + else { + return VpCheckGetValue(NewZeroWrapLimited(1, n)); + } } if (is_zero(vexp)) { - return VpCheckGetValue(VpCreateRbObject(n, "1", true)); + return VpCheckGetValue(NewOneWrapLimited(1, n)); } else if (is_one(vexp)) { - return self; + return self; } if (VpIsInf(x)) { - if (is_negative(vexp)) { - if (BIGDECIMAL_NEGATIVE_P(x)) { - if (is_integer(vexp)) { - if (is_even(vexp)) { - /* (-Infinity) ** (-even_integer) -> +0 */ - return VpCheckGetValue(VpCreateRbObject(n, "0", true)); - } - else { - /* (-Infinity) ** (-odd_integer) -> -0 */ - return VpCheckGetValue(VpCreateRbObject(n, "-0", true)); - } - } - else { - /* (-Infinity) ** (-non_integer) -> -0 */ - return VpCheckGetValue(VpCreateRbObject(n, "-0", true)); - } - } - else { - return VpCheckGetValue(VpCreateRbObject(n, "0", true)); - } - } - else { - y = VpCreateRbObject(n, "0", true); - if (BIGDECIMAL_NEGATIVE_P(x)) { - if (is_integer(vexp)) { - if (is_even(vexp)) { - VpSetPosInf(y); - } - else { - VpSetNegInf(y); - } - } - else { - /* TODO: support complex */ - rb_raise(rb_eMathDomainError, - "a non-integral exponent for a negative base"); - } - } - else { - VpSetPosInf(y); - } + if (is_negative(vexp)) { + if (BIGDECIMAL_NEGATIVE_P(x)) { + if (is_integer(vexp)) { + if (is_even(vexp)) { + /* (-Infinity) ** (-even_integer) -> +0 */ + return VpCheckGetValue(NewZeroWrapLimited(1, n)); + } + else { + /* (-Infinity) ** (-odd_integer) -> -0 */ + return VpCheckGetValue(NewZeroWrapLimited(-1, n)); + } + } + else { + /* (-Infinity) ** (-non_integer) -> -0 */ + return VpCheckGetValue(NewZeroWrapLimited(-1, n)); + } + } + else { + return VpCheckGetValue(NewZeroWrapLimited(1, n)); + } + } + else { + y = NewZeroWrapLimited(1, n); + if (BIGDECIMAL_NEGATIVE_P(x)) { + if (is_integer(vexp)) { + if (is_even(vexp)) { + VpSetPosInf(y); + } + else { + VpSetNegInf(y); + } + } + else { + /* TODO: support complex */ + rb_raise(rb_eMathDomainError, + "a non-integral exponent for a negative base"); + } + } + else { + VpSetPosInf(y); + } return VpCheckGetValue(y); - } + } } if (exp != NULL) { - return bigdecimal_power_by_bigdecimal(x, exp, n); + return bigdecimal_power_by_bigdecimal(x, exp, n); } else if (RB_TYPE_P(vexp, T_BIGNUM)) { - VALUE abs_value = BigDecimal_abs(self); - if (is_one(abs_value)) { - return VpCheckGetValue(VpCreateRbObject(n, "1", true)); - } - else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) { - if (is_negative(vexp)) { - y = VpCreateRbObject(n, "0", true); - if (is_even(vexp)) { - VpSetInf(y, VpGetSign(x)); - } - else { - VpSetInf(y, -VpGetSign(x)); - } + VALUE abs_value = BigDecimal_abs(self); + if (is_one(abs_value)) { + return VpCheckGetValue(NewOneWrapLimited(1, n)); + } + else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) { + if (is_negative(vexp)) { + y = NewZeroWrapLimited(1, n); + VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x)); return VpCheckGetValue(y); - } - else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { - return VpCheckGetValue(VpCreateRbObject(n, "-0", true)); - } - else { - return VpCheckGetValue(VpCreateRbObject(n, "0", true)); - } - } - else { - if (is_positive(vexp)) { - y = VpCreateRbObject(n, "0", true); - if (is_even(vexp)) { - VpSetInf(y, VpGetSign(x)); - } - else { - VpSetInf(y, -VpGetSign(x)); - } + } + else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { + return VpCheckGetValue(NewZeroWrapLimited(-1, n)); + } + else { + return VpCheckGetValue(NewZeroWrapLimited(1, n)); + } + } + else { + if (is_positive(vexp)) { + y = NewZeroWrapLimited(1, n); + VpSetInf(y, (is_even(vexp) ? 1 : -1) * VpGetSign(x)); return VpCheckGetValue(y); - } - else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { - return VpCheckGetValue(VpCreateRbObject(n, "-0", true)); - } - else { - return VpCheckGetValue(VpCreateRbObject(n, "0", true)); - } - } + } + else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { + return VpCheckGetValue(NewZeroWrapLimited(-1, n)); + } + else { + return VpCheckGetValue(NewZeroWrapLimited(1, n)); + } + } } int_exp = FIX2LONG(vexp); @@ -3091,15 +3186,15 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) if (ma == 0) ma = 1; if (VpIsDef(x)) { - mp = x->Prec * (VpBaseFig() + 1); - GUARD_OBJ(y, VpCreateRbObject(mp * (ma + 1), "0", true)); + mp = x->Prec * (VpBaseFig() + 1); + GUARD_OBJ(y, NewZeroWrapLimited(1, mp * (ma + 1))); } else { - GUARD_OBJ(y, VpCreateRbObject(1, "0", true)); + GUARD_OBJ(y, NewZeroWrapLimited(1, 1)); } VpPowerByInt(y, x, int_exp); if (!NIL_P(prec) && VpIsDef(y)) { - VpMidRound(y, VpGetRoundMode(), n); + VpMidRound(y, VpGetRoundMode(), n); } return VpCheckGetValue(y); } @@ -3850,18 +3945,16 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec) return VpCheckGetValue(GetVpValueWithPrec(INT2FIX(0), prec, 1)); } else { - Real* vy; - vy = VpCreateRbObject(prec, "#0", true); + Real* vy = NewZeroWrapNolimit(1, prec); VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE); RB_GC_GUARD(vy->obj); return VpCheckGetValue(vy); } } else if (nan) { - Real* vy; - vy = VpCreateRbObject(prec, "#0", true); - VpSetNaN(vy); - RB_GC_GUARD(vy->obj); + Real* vy = NewZeroWrapNolimit(1, prec); + VpSetNaN(vy); + RB_GC_GUARD(vy->obj); return VpCheckGetValue(vy); } else if (vx == NULL) { @@ -3879,7 +3972,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec) VpSetSign(vx, 1); } - one = VpCheckGetValue(VpCreateRbObject(1, "1", true)); + one = VpCheckGetValue(NewOneWrapLimited(1, 1)); y = one; d = y; i = 1; @@ -4006,15 +4099,13 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec) break; } if (infinite && !negative) { - Real* vy; - vy = VpCreateRbObject(prec, "#0", true); + Real *vy = NewZeroWrapNolimit(1, prec); RB_GC_GUARD(vy->obj); VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE); return VpCheckGetValue(vy); } else if (nan) { - Real* vy; - vy = VpCreateRbObject(prec, "#0", true); + Real* vy = NewZeroWrapNolimit(1, prec); RB_GC_GUARD(vy->obj); VpSetNaN(vy); return VpCheckGetValue(vy); @@ -4028,7 +4119,7 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec) } x = VpCheckGetValue(vx); - RB_GC_GUARD(one) = VpCheckGetValue(VpCreateRbObject(1, "1", true)); + RB_GC_GUARD(one) = VpCheckGetValue(NewOneWrapLimited(1, 1)); RB_GC_GUARD(two) = VpCheckGetValue(VpCreateRbObject(1, "2", true)); n = prec + BIGDECIMAL_DOUBLE_FIGURES; @@ -4957,10 +5048,10 @@ VpInit(DECDIG BaseVal) VpGetDoubleNegZero(); /* Const 1.0 */ - VpConstOne = rbd_allocate_struct_one(1, 1); + VpConstOne = NewOneNolimit(1, 1); /* Const 0.5 */ - VpConstPt5 = rbd_allocate_struct_one(1, 1); + VpConstPt5 = NewOneNolimit(1, 1); VpConstPt5->exponent = 0; VpConstPt5->frac[0] = 5*BASE1; @@ -5094,19 +5185,15 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) char v, *psz; int sign=1; Real *vp = NULL; - size_t prec_limit = VpGetPrecLimit(); VALUE buf; - len = (mx + BASE_FIG - 1) / BASE_FIG; /* Determine allocation unit. */ - if (len == 0) ++len; - if (szVal == NULL) { return_zero: /* necessary to be able to store */ /* at least mx digits. */ /* szVal==NULL ==> allocate zero value. */ vp = rbd_allocate_struct(mx); - vp->MaxPrec = len; /* set max precision */ + vp->MaxPrec = rbd_calculate_internal_digits(mx, false); /* Must false */ VpSetZero(vp, 1); /* initialize vp to zero. */ return vp; } @@ -5121,14 +5208,10 @@ VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) /* Processing the leading one `#` */ if (*szVal != '#') { - if (prec_limit) { - size_t const max_len = (prec_limit + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */ - if (len > max_len) { - len = max_len; - } - } + len = rbd_calculate_internal_digits(mx, true); } else { + len = rbd_calculate_internal_digits(mx, false); ++szVal; } @@ -5862,7 +5945,7 @@ VpMult(Real *c, Real *a, Real *b) if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */ w = c; - c = rbd_allocate_struct_zero((size_t)((MxIndAB + 1) * BASE_FIG), 1); + c = NewZeroNolimit(1, (size_t)((MxIndAB + 1) * BASE_FIG)); MxIndC = MxIndAB; } @@ -7028,8 +7111,8 @@ VpSqrt(Real *y, Real *x) /* allocate temporally variables */ /* TODO: reconsider MaxPrec of f and r */ - f = rbd_allocate_struct_one(y->MaxPrec * (BASE_FIG + 2), 1); - r = rbd_allocate_struct_one((n + n) * (BASE_FIG + 2), 1); + f = NewOneNolimit(1, y->MaxPrec * (BASE_FIG + 2)); + r = NewOneNolimit(1, (n + n) * (BASE_FIG + 2)); nr = 0; y_prec = y->MaxPrec; @@ -7486,8 +7569,8 @@ VpPowerByInt(Real *y, Real *x, SIGNED_VALUE n) /* Allocate working variables */ /* TODO: reconsider MaxPrec of w1 and w2 */ - w1 = rbd_allocate_struct_zero((y->MaxPrec + 2) * BASE_FIG, 1); - w2 = rbd_allocate_struct_zero((w1->MaxPrec * 2 + 1) * BASE_FIG, 1); + w1 = NewZeroNolimit(1, (y->MaxPrec + 2) * BASE_FIG); + w2 = NewZeroNolimit(1, (w1->MaxPrec * 2 + 1) * BASE_FIG); /* calculation start */ From d70a4d53e5b63d4512830ecf0e04dbfae8b02130 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Tue, 15 Nov 2022 09:32:53 +0900 Subject: [PATCH 28/33] Mark some functions MAYBE_UNUSED --- ext/bigdecimal/bigdecimal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index a5b80464..c85ef159 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -217,6 +217,7 @@ rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision) return real; } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits)); #define NewZeroLimited rbd_allocate_struct_zero_limited static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits) @@ -224,6 +225,7 @@ rbd_allocate_struct_zero_limited(int sign, size_t const digits) return rbd_allocate_struct_zero(sign, digits, true); } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits)); #define NewZeroNolimit rbd_allocate_struct_zero_nolimit static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits) @@ -242,6 +244,7 @@ rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision) return real; } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits)); #define NewOneLimited rbd_allocate_struct_one_limited static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits) @@ -249,6 +252,7 @@ rbd_allocate_struct_one_limited(int sign, size_t const digits) return rbd_allocate_struct_one(sign, digits, true); } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits)); #define NewOneNolimit rbd_allocate_struct_one_nolimit static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits) @@ -320,6 +324,7 @@ rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, return real; } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)); #define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits) @@ -327,6 +332,7 @@ rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits) return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true); } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits)); #define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits) @@ -345,6 +351,7 @@ rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, b return real; } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits)); #define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits) @@ -352,6 +359,7 @@ rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits) return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true); } +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits)); #define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits) From d6f5bb40c7598a487b468460294db66d8fc955f5 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Tue, 15 Nov 2022 13:01:50 +0900 Subject: [PATCH 29/33] Replace sprintf by snprintf --- ext/bigdecimal/bigdecimal.c | 302 ++++++++++++++++++++++-------------- ext/bigdecimal/bigdecimal.h | 8 +- 2 files changed, 189 insertions(+), 121 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index c85ef159..9b1d33ed 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -420,7 +420,7 @@ GetVpValueWithPrec(VALUE v, long prec, int must) case T_FIXNUM: { char szD[128]; - sprintf(szD, "%ld", FIX2LONG(v)); + snprintf(szD, 128, "%ld", FIX2LONG(v)); v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must); break; } @@ -779,13 +779,15 @@ BigDecimal_dump(int argc, VALUE *argv, VALUE self) char *psz; VALUE dummy; volatile VALUE dump; + size_t len; rb_scan_args(argc, argv, "01", &dummy); GUARD_OBJ(vp,GetVpValue(self, 1)); dump = rb_str_new(0, VpNumOfChars(vp, "E")+50); psz = RSTRING_PTR(dump); - sprintf(psz, "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig()); - VpToString(vp, psz+strlen(psz), 0, 0); + snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig()); + len = strlen(psz); + VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0); rb_str_resize(dump, strlen(psz)); return dump; } @@ -1305,7 +1307,7 @@ BigDecimal_to_f(VALUE self) str = rb_str_new(0, VpNumOfChars(p, "E")); buf = RSTRING_PTR(str); - VpToString(p, buf, 0, 0); + VpToString(p, buf, RSTRING_LEN(str), 0, 0); errno = 0; d = strtod(buf, 0); if (errno == ERANGE) { @@ -2753,10 +2755,10 @@ BigDecimal_to_s(int argc, VALUE *argv, VALUE self) psz = RSTRING_PTR(str); if (fmt) { - VpToFString(vp, psz, mc, fPlus); + VpToFString(vp, psz, RSTRING_LEN(str), mc, fPlus); } else { - VpToString (vp, psz, mc, fPlus); + VpToString (vp, psz, RSTRING_LEN(str), mc, fPlus); } rb_str_resize(str, strlen(psz)); return str; @@ -2798,7 +2800,7 @@ BigDecimal_split(VALUE self) GUARD_OBJ(vp, GetVpValue(self, 1)); str = rb_str_new(0, VpNumOfChars(vp, "E")); psz1 = RSTRING_PTR(str); - VpSzMantissa(vp, psz1); + VpSzMantissa(vp, psz1, RSTRING_LEN(str)); s = 1; if(psz1[0] == '-') { size_t len = strlen(psz1 + 1); @@ -2847,7 +2849,7 @@ BigDecimal_inspect(VALUE self) nc = VpNumOfChars(vp, "E"); str = rb_str_new(0, nc); - VpToString(vp, RSTRING_PTR(str), 0, 0); + VpToString(vp, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0); rb_str_resize(str, strlen(RSTRING_PTR(str))); return str; } @@ -6528,188 +6530,254 @@ VpExponent10(Real *a) } VP_EXPORT void -VpSzMantissa(Real *a,char *psz) +VpSzMantissa(Real *a, char *buf, size_t buflen) { size_t i, n, ZeroSup; DECDIG_DBL m, e, nn; if (VpIsNaN(a)) { - sprintf(psz, SZ_NaN); - return; + snprintf(buf, buflen, SZ_NaN); + return; } if (VpIsPosInf(a)) { - sprintf(psz, SZ_INF); + snprintf(buf, buflen, SZ_INF); return; } if (VpIsNegInf(a)) { - sprintf(psz, SZ_NINF); + snprintf(buf, buflen, SZ_NINF); return; } ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ if (!VpIsZero(a)) { - if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; - n = a->Prec; - for (i = 0; i < n; ++i) { - m = BASE1; - e = a->frac[i]; - while (m) { - nn = e / m; - if (!ZeroSup || nn) { - sprintf(psz, "%lu", (unsigned long)nn); /* The leading zero(s) */ - psz += strlen(psz); - /* as 0.00xx will be ignored. */ - ZeroSup = 0; /* Set to print succeeding zeros */ - } - e = e - nn * m; - m /= 10; - } - } - *psz = 0; - while (psz[-1] == '0') *(--psz) = 0; + if (BIGDECIMAL_NEGATIVE_P(a)) *buf++ = '-'; + n = a->Prec; + for (i = 0; i < n; ++i) { + m = BASE1; + e = a->frac[i]; + while (m) { + nn = e / m; + if (!ZeroSup || nn) { + snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */ + buf += strlen(buf); + /* as 0.00xx will be ignored. */ + ZeroSup = 0; /* Set to print succeeding zeros */ + } + e = e - nn * m; + m /= 10; + } + } + *buf = 0; + while (buf[-1] == '0') *(--buf) = 0; } else { - if (VpIsPosZero(a)) sprintf(psz, "0"); - else sprintf(psz, "-0"); + if (VpIsPosZero(a)) snprintf(buf, buflen, "0"); + else snprintf(buf, buflen, "-0"); } } VP_EXPORT int -VpToSpecialString(Real *a,char *psz,int fPlus) +VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus) /* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */ { if (VpIsNaN(a)) { - sprintf(psz,SZ_NaN); - return 1; + snprintf(buf, buflen, SZ_NaN); + return 1; } if (VpIsPosInf(a)) { - if (fPlus == 1) { - *psz++ = ' '; - } - else if (fPlus == 2) { - *psz++ = '+'; - } - sprintf(psz, SZ_INF); - return 1; + if (fPlus == 1) { + *buf++ = ' '; + } + else if (fPlus == 2) { + *buf++ = '+'; + } + snprintf(buf, buflen, SZ_INF); + return 1; } if (VpIsNegInf(a)) { - sprintf(psz, SZ_NINF); - return 1; + snprintf(buf, buflen, SZ_NINF); + return 1; } if (VpIsZero(a)) { - if (VpIsPosZero(a)) { - if (fPlus == 1) sprintf(psz, " 0.0"); - else if (fPlus == 2) sprintf(psz, "+0.0"); - else sprintf(psz, "0.0"); - } - else sprintf(psz, "-0.0"); - return 1; + if (VpIsPosZero(a)) { + if (fPlus == 1) snprintf(buf, buflen, " 0.0"); + else if (fPlus == 2) snprintf(buf, buflen, "+0.0"); + else snprintf(buf, buflen, "0.0"); + } + else snprintf(buf, buflen, "-0.0"); + return 1; } return 0; } VP_EXPORT void -VpToString(Real *a, char *psz, size_t fFmt, int fPlus) +VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus) /* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */ { size_t i, n, ZeroSup; DECDIG shift, m, e, nn; - char *pszSav = psz; + char *p = buf; + size_t plen = buflen; ssize_t ex; - if (VpToSpecialString(a, psz, fPlus)) return; + if (VpToSpecialString(a, buf, buflen, fPlus)) return; ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ - if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; - else if (fPlus == 1) *psz++ = ' '; - else if (fPlus == 2) *psz++ = '+'; +#define ADVANCE(n) do { \ + if (plen < n) goto overflow; \ + p += n; \ + plen -= n; \ +} while (0) + + if (BIGDECIMAL_NEGATIVE_P(a)) { + *p = '-'; + ADVANCE(1); + } + else if (fPlus == 1) { + *p = ' '; + ADVANCE(1); + } + else if (fPlus == 2) { + *p = '+'; + ADVANCE(1); + } + + *p = '0'; ADVANCE(1); + *p = '.'; ADVANCE(1); - *psz++ = '0'; - *psz++ = '.'; n = a->Prec; for (i = 0; i < n; ++i) { - m = BASE1; - e = a->frac[i]; - while (m) { - nn = e / m; - if (!ZeroSup || nn) { - sprintf(psz, "%lu", (unsigned long)nn); /* The reading zero(s) */ - psz += strlen(psz); - /* as 0.00xx will be ignored. */ - ZeroSup = 0; /* Set to print succeeding zeros */ - } - e = e - nn * m; - m /= 10; - } + m = BASE1; + e = a->frac[i]; + while (m) { + nn = e / m; + if (!ZeroSup || nn) { + /* The reading zero(s) */ + size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn); + if (n > plen) goto overflow; + ADVANCE(n); + /* as 0.00xx will be ignored. */ + ZeroSup = 0; /* Set to print succeeding zeros */ + } + e = e - nn * m; + m /= 10; + } } + ex = a->exponent * (ssize_t)BASE_FIG; shift = BASE1; while (a->frac[0] / shift == 0) { - --ex; - shift /= 10; + --ex; + shift /= 10; } - while (psz[-1] == '0') { - *(--psz) = 0; + while (p - 1 > buf && p[-1] == '0') { + *(--p) = '\0'; + ++plen; } - sprintf(psz, "e%"PRIdSIZE, ex); - if (fFmt) VpFormatSt(pszSav, fFmt); + snprintf(p, plen, "e%"PRIdSIZE, ex); + if (fFmt) VpFormatSt(buf, fFmt); + + overflow: + return; +#undef ADVANCE } VP_EXPORT void -VpToFString(Real *a, char *psz, size_t fFmt, int fPlus) +VpToFString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus) /* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */ { size_t i, n; DECDIG m, e, nn; - char *pszSav = psz; + char *p = buf; + size_t plen = buflen; ssize_t ex; - if (VpToSpecialString(a, psz, fPlus)) return; + if (VpToSpecialString(a, buf, buflen, fPlus)) return; + +#define ADVANCE(n) do { \ + if (plen < n) goto overflow; \ + p += n; \ + plen -= n; \ +} while (0) - if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; - else if (fPlus == 1) *psz++ = ' '; - else if (fPlus == 2) *psz++ = '+'; + + if (BIGDECIMAL_NEGATIVE_P(a)) { + *p = '-'; + ADVANCE(1); + } + else if (fPlus == 1) { + *p = ' '; + ADVANCE(1); + } + else if (fPlus == 2) { + *p = '+'; + ADVANCE(1); + } n = a->Prec; ex = a->exponent; if (ex <= 0) { - *psz++ = '0';*psz++ = '.'; - while (ex < 0) { - for (i=0; i < BASE_FIG; ++i) *psz++ = '0'; - ++ex; - } - ex = -1; + *p = '0'; ADVANCE(1); + *p = '.'; ADVANCE(1); + while (ex < 0) { + for (i=0; i < BASE_FIG; ++i) { + *p = '0'; ADVANCE(1); + } + ++ex; + } + ex = -1; } for (i = 0; i < n; ++i) { - --ex; - if (i == 0 && ex >= 0) { - sprintf(psz, "%lu", (unsigned long)a->frac[i]); - psz += strlen(psz); - } - else { - m = BASE1; - e = a->frac[i]; - while (m) { - nn = e / m; - *psz++ = (char)(nn + '0'); - e = e - nn * m; - m /= 10; - } - } - if (ex == 0) *psz++ = '.'; + --ex; + if (i == 0 && ex >= 0) { + size_t n = snprintf(p, plen, "%lu", (unsigned long)a->frac[i]); + if (n > plen) goto overflow; + ADVANCE(n); + } + else { + m = BASE1; + e = a->frac[i]; + while (m) { + nn = e / m; + *p = (char)(nn + '0'); + ADVANCE(1); + e = e - nn * m; + m /= 10; + } + } + if (ex == 0) { + *p = '.'; + ADVANCE(1); + } } while (--ex>=0) { - m = BASE; - while (m /= 10) *psz++ = '0'; - if (ex == 0) *psz++ = '.'; - } - *psz = 0; - while (psz[-1] == '0') *(--psz) = 0; - if (psz[-1] == '.') sprintf(psz, "0"); - if (fFmt) VpFormatSt(pszSav, fFmt); + m = BASE; + while (m /= 10) { + *p = '0'; + ADVANCE(1); + } + if (ex == 0) { + *p = '.'; + ADVANCE(1); + } + } + + *p = '\0'; + while (p - 1 > buf && p[-1] == '0') { + *(--p) = '\0'; + ++plen; + } + if (p - 1 > buf && p[-1] == '.') { + snprintf(p, plen, "0"); + } + if (fFmt) VpFormatSt(buf, fFmt); + + overflow: + return; +#undef ADVANCE } /* diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index 46fafde1..54fed811 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -231,10 +231,10 @@ VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b); VP_EXPORT size_t VpDivd(Real *c,Real *r,Real *a,Real *b); VP_EXPORT int VpComp(Real *a,Real *b); VP_EXPORT ssize_t VpExponent10(Real *a); -VP_EXPORT void VpSzMantissa(Real *a,char *psz); -VP_EXPORT int VpToSpecialString(Real *a,char *psz,int fPlus); -VP_EXPORT void VpToString(Real *a, char *psz, size_t fFmt, int fPlus); -VP_EXPORT void VpToFString(Real *a, char *psz, size_t fFmt, int fPlus); +VP_EXPORT void VpSzMantissa(Real *a, char *buf, size_t bufsize); +VP_EXPORT int VpToSpecialString(Real *a, char *buf, size_t bufsize, int fPlus); +VP_EXPORT void VpToString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus); +VP_EXPORT void VpToFString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus); VP_EXPORT int VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne); VP_EXPORT int VpVtoD(double *d, SIGNED_VALUE *e, Real *m); VP_EXPORT void VpDtoV(Real *m,double d); From b2123faa5206a1c4cb85cd37de7354125011fd1f Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Tue, 15 Nov 2022 15:58:56 +0900 Subject: [PATCH 30/33] Add fallback definition of MAYBE_UNUSED --- ext/bigdecimal/bigdecimal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 9b1d33ed..d6ea35c6 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -107,6 +107,10 @@ static struct { # define RB_OBJ_STRING(obj) StringValueCStr(obj) #endif +#ifndef MAYBE_UNUSED +# define MAYBE_UNUSED(x) x +#endif + #define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0) #define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0) From 20eb3e4ddbce06280a8b1491e66a8e5d8a004629 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 29 Nov 2022 12:04:40 +0900 Subject: [PATCH 31/33] Added dependebot for github actions --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..b18fd293 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' From d484f25a03433bed24ab98662fc640a979eb9156 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 03:15:41 +0000 Subject: [PATCH 32/33] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 5ce8fde8..552299a0 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -33,7 +33,7 @@ jobs: - { os: windows-latest , ruby: debug } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87658190..bb0fca1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - { os: windows-latest , ruby: debug } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 From 25a75c2033512d1b9b47ede3a26da68de06ff342 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 5 Dec 2022 19:44:05 +0900 Subject: [PATCH 33/33] Bump version to 3.1.3 --- bigdecimal.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bigdecimal.gemspec b/bigdecimal.gemspec index 1feed332..d2157571 100644 --- a/bigdecimal.gemspec +++ b/bigdecimal.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = "bigdecimal" - s.version = "3.1.2" + s.version = "3.1.3" s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"] s.email = ["mrkn@mrkn.jp"]