Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions spec/std/humanize_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ describe Number do
it { assert_prints Float64::NAN.format, "NaN" }

it { assert_prints "12345.67890123456789012345".to_big_d.format, "12,345.67890123456789012345" }
it { assert_prints "12345.67890123456789012345".to_big_d.format(decimal_places: 10), "12,345.6789012346" }
it { assert_prints "12345.67890123456789012345".to_big_d.format(decimal_places: -2), "12,300" }

it { assert_prints "12345.67890123456789012345e+20".to_big_d.format, "1,234,567,890,123,456,789,012,345.0" }
it { assert_prints "12345.67890123456789012345e+20".to_big_d.format(decimal_places: 10), "1,234,567,890,123,456,789,012,345.0000000000" }
it { assert_prints "12345.67890123456789012345e+20".to_big_d.format(decimal_places: -20), "1,234,600,000,000,000,000,000,000" }

it { assert_prints "12345.67890123456789012345e-10".to_big_d.format, "0.000001234567890123456789012345" }
it { assert_prints "12345.67890123456789012345e-10".to_big_d.format(decimal_places: 40), "0.0000012345678901234567890123450000000000" }
it { assert_prints "12345.67890123456789012345e-10".to_big_d.format(decimal_places: 10), "0.0000012346" }

it "extracts integer part correctly (#12997)" do
assert_prints 1.9999998.format, "1.9999998"
Expand Down
32 changes: 31 additions & 1 deletion src/big/big_decimal.cr
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,15 @@ struct BigDecimal < Number
end

def to_s(io : IO) : Nil
to_s_impl(io, point_range: -3..15)
end

protected def to_s_impl(*, point_range : Range) : String
String.build { |io| to_s_impl(io, point_range: point_range) }
end

# TODO: refactor into `Float::Printer.shortest`
protected def to_s_impl(io : IO, *, point_range : Range) : Nil
factor_powers_of_ten

cstr = LibGMP.get_str(nil, 10, @value)
Expand All @@ -516,7 +525,7 @@ struct BigDecimal < Number
decimal_exponent = length.to_i - @scale
point = decimal_exponent
exp = point
exp_mode = point > 15 || point < -3
exp_mode = !point_range.includes?(point)
point = 1 if exp_mode

# add leading zero
Expand Down Expand Up @@ -561,6 +570,27 @@ struct BigDecimal < Number
end
end

# :inherit:
def format(io : IO, separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : Nil
number = self
if decimal_places
number = number.round(decimal_places)
end

if decimal_places && decimal_places >= 0
string = number.abs.to_s_impl(point_range: ..)
integer, _, decimals = string.partition('.')
else
string = number.to_s_impl(point_range: ..)
_, _, decimals = string.partition(".")
integer = number.trunc.to_big_i.abs.to_s
end

is_negative = number < 0

format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)
end

# Converts to `BigInt`. Truncates anything on the right side of the decimal point.
def to_big_i : BigInt
trunc.value
Expand Down
22 changes: 14 additions & 8 deletions src/humanize.cr
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,23 @@ struct Number
integer, _, decimals = string.partition('.')
end

is_negative = number.is_a?(Float::Primitive) ? Math.copysign(1, number) < 0 : number < 0

format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)
end

# :ditto:
def format(separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : String
String.build do |io|
format(io, separator, delimiter, decimal_places, group: group, only_significant: only_significant)
end
end

private def format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant) : Nil
int_size = integer.size
dec_size = decimals.size

io << '-' if number.is_a?(Float::Primitive) ? Math.copysign(1, number) < 0 : number < 0
io << '-' if is_negative

start = int_size % group
start += group if start == 0
Expand Down Expand Up @@ -91,13 +104,6 @@ struct Number
end
end

# :ditto:
def format(separator = '.', delimiter = ',', decimal_places : Int? = nil, *, group : Int = 3, only_significant : Bool = false) : String
String.build do |io|
format(io, separator, delimiter, decimal_places, group: group, only_significant: only_significant)
end
end

# Default SI prefixes ordered by magnitude.
SI_PREFIXES = { {'q', 'r', 'y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm'}, {nil, 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'} }

Expand Down