diff --git a/spec/std/big/big_decimal_spec.cr b/spec/std/big/big_decimal_spec.cr index 3f4f11aca033..e783d2f5bb10 100644 --- a/spec/std/big/big_decimal_spec.cr +++ b/spec/std/big/big_decimal_spec.cr @@ -1,5 +1,6 @@ require "spec" require "big" +require "../../support/string" describe BigDecimal do it "initializes from valid input" do @@ -382,34 +383,45 @@ describe BigDecimal do end it "converts to string" do - BigDecimal.new.to_s.should eq "0" - BigDecimal.new(0).to_s.should eq "0" - BigDecimal.new(1).to_s.should eq "1" - BigDecimal.new(-1).to_s.should eq "-1" - BigDecimal.new("8.5").to_s.should eq "8.5" - BigDecimal.new("-0.35").to_s.should eq "-0.35" - BigDecimal.new("-.35").to_s.should eq "-0.35" - BigDecimal.new("0.01").to_s.should eq "0.01" - BigDecimal.new("-0.01").to_s.should eq "-0.01" - BigDecimal.new("0.00123").to_s.should eq "0.00123" - BigDecimal.new("-0.00123").to_s.should eq "-0.00123" - BigDecimal.new("1.0").to_s.should eq "1" - BigDecimal.new("-1.0").to_s.should eq "-1" - BigDecimal.new("1.000").to_s.should eq "1" - BigDecimal.new("-1.000").to_s.should eq "-1" - BigDecimal.new("1.0001").to_s.should eq "1.0001" - BigDecimal.new("-1.0001").to_s.should eq "-1.0001" - - (BigDecimal.new(1).div(BigDecimal.new(3), 9)).to_s.should eq "0.333333333" - (BigDecimal.new(1000).div(BigDecimal.new(3000), 9)).to_s.should eq "0.333333333" - (BigDecimal.new(1).div(BigDecimal.new(3000), 9)).to_s.should eq "0.000333333" - - (BigDecimal.new("112839719283").div(BigDecimal.new("3123779"), 9)).to_s.should eq "36122.824080384" - (BigDecimal.new("112839719283").div(BigDecimal.new("3123779"), 14)).to_s.should eq "36122.8240803846879" - (BigDecimal.new("-0.4098").div(BigDecimal.new("0.2229011193"), 20)).to_s.should eq "-1.83848336557007141059" - - BigDecimal.new(1, 2).to_s.should eq "0.01" - BigDecimal.new(100, 4).to_s.should eq "0.01" + assert_prints BigDecimal.new.to_s, "0.0" + assert_prints BigDecimal.new(0).to_s, "0.0" + assert_prints BigDecimal.new(1).to_s, "1.0" + assert_prints BigDecimal.new(-1).to_s, "-1.0" + assert_prints BigDecimal.new("8.5").to_s, "8.5" + assert_prints BigDecimal.new("-0.35").to_s, "-0.35" + assert_prints BigDecimal.new("-.35").to_s, "-0.35" + assert_prints BigDecimal.new("0.01").to_s, "0.01" + assert_prints BigDecimal.new("-0.01").to_s, "-0.01" + assert_prints BigDecimal.new("0.00123").to_s, "0.00123" + assert_prints BigDecimal.new("-0.00123").to_s, "-0.00123" + assert_prints BigDecimal.new("1.0").to_s, "1.0" + assert_prints BigDecimal.new("-1.0").to_s, "-1.0" + assert_prints BigDecimal.new("1.000").to_s, "1.0" + assert_prints BigDecimal.new("-1.000").to_s, "-1.0" + assert_prints BigDecimal.new("1.0001").to_s, "1.0001" + assert_prints BigDecimal.new("-1.0001").to_s, "-1.0001" + + assert_prints BigDecimal.new(1).div(BigDecimal.new(3), 9).to_s, "0.333333333" + assert_prints BigDecimal.new(1000).div(BigDecimal.new(3000), 9).to_s, "0.333333333" + assert_prints BigDecimal.new(1).div(BigDecimal.new(3000), 9).to_s, "0.000333333" + + assert_prints BigDecimal.new("112839719283").div(BigDecimal.new("3123779"), 9).to_s, "36122.824080384" + assert_prints BigDecimal.new("112839719283").div(BigDecimal.new("3123779"), 14).to_s, "36122.8240803846879" + assert_prints BigDecimal.new("-0.4098").div(BigDecimal.new("0.2229011193"), 20).to_s, "-1.83848336557007141059" + + assert_prints BigDecimal.new(1, 2).to_s, "0.01" + assert_prints BigDecimal.new(100, 4).to_s, "0.01" + + assert_prints "12345678901234567".to_big_d.to_s, "1.2345678901234567e+16" + assert_prints "1234567890123456789".to_big_d.to_s, "1.234567890123456789e+18" + + assert_prints BigDecimal.new(1_000_000_000_000_000_i64, 0).to_s, "1.0e+15" + assert_prints BigDecimal.new(100_000_000_000_000_i64, 0).to_s, "100000000000000.0" + assert_prints BigDecimal.new(1, 4).to_s, "0.0001" + assert_prints BigDecimal.new(1, 5).to_s, "1.0e-5" + + assert_prints "1.23e45".to_big_d.to_s, "1.23e+45" + assert_prints "1e-234".to_big_d.to_s, "1.0e-234" end it "converts to other number types" do @@ -781,6 +793,6 @@ describe BigDecimal do end describe "#inspect" do - it { "123".to_big_d.inspect.should eq("123") } + it { "123".to_big_d.inspect.should eq("123.0") } end end diff --git a/src/big/big_decimal.cr b/src/big/big_decimal.cr index 66945bcc6899..1cb44c0b1657 100644 --- a/src/big/big_decimal.cr +++ b/src/big/big_decimal.cr @@ -443,28 +443,62 @@ struct BigDecimal < Number def to_s(io : IO) : Nil factor_powers_of_ten - s = @value.to_s - if @scale == 0 - io << s - return + cstr = LibGMP.get_str(nil, 10, @value) + length = LibC.strlen(cstr) + buffer = Slice.new(cstr, length) + + # add negative sign + if buffer[0]? == 45 # '-' + io << '-' + buffer = buffer[1..] + length -= 1 end - if @scale >= s.size && @value >= 0 - io << "0." - (@scale - s.size).times do - io << '0' - end - io << s - elsif @scale >= s.size && @value < 0 - io << "-0.0" - (@scale - s.size).times do - io << '0' - end - io << s[1..-1] - elsif (offset = s.size - @scale) == 1 && @value < 0 - io << "-0." << s[offset..-1] - else - io << s[0...offset] << '.' << s[offset..-1] + decimal_exponent = length.to_i - @scale + point = decimal_exponent + exp = point + exp_mode = point > 15 || point < -3 + point = 1 if exp_mode + + # add leading zero + io << '0' if point < 1 + + # add integer part digits + if decimal_exponent > 0 && !exp_mode + # whole number but not big enough to be exp form + io.write_string buffer[0, {decimal_exponent, length}.min] + buffer = buffer[{decimal_exponent, length}.min...] + (point - length).times { io << '0' } + elsif point > 0 + io.write_string buffer[0, point] + buffer = buffer[point...] + end + + io << '.' + + # add leading zeros after point + if point < 0 + (-point).times { io << '0' } + end + + # remove trailing zeroes + while buffer.size > 1 && buffer.last === '0' + buffer = buffer[0..-2] + end + + # add fractional part digits + io.write_string buffer + + # print trailing 0 if whole number or exp notation of power of ten + if (decimal_exponent >= length && !exp_mode) || (exp != point && length == 1) + io << '0' + end + + # exp notation + if exp != point + io << 'e' + io << '+' if exp > 0 + (exp - 1).to_s(io) end end