Class: Numeric

Inherits:
Object
  • Object
show all
Defined in:
lib/util.rb

Overview

Addes Numeirc class to #strfnum method.

Direct Known Subclasses

BigDecimal

Instance Method Summary collapse

Instance Method Details

#strfnum(format) ⇒ String

Converts the numeric value of this instance to the string representation, using the numeric format strings.

Numeric Format Strings

Uses the following characters as numeric format strings.

"0" (Zero Placeholder)

Replaces "0" with the corresponding digit if one is present; otherwize, zero appears.

"#" (Digit Placeholder)

Replaces "#" with the corresponding digit if one is present; otherwise, no digit appears.

"." (Decimal Point)

Determines the location of the decimal separator.

"," (Group Separator)

Serves as both a group separator if zero or sharp appears between comma and dot or format strings does not include dot; otherwise, serves as number scaling specifier. If format strings includes a group separator, it inserts a group separater character between each group. Including a number scaling specifier, it divides a number by 1000 (it depends on group size) for each comma specified.

"%" (Percentage Placeholder)

Multiplies a number by 100 and inserts a percentage symbol.

"\" (Escape Character)

Causes the next character to be interpreted as a literal.

";" (Section Separater)

Defines sections with separate format strings for positive, negative, and zero numbers.

Other Characters

The character is copied to the result string unchanged.

3.141592.strfnum('0.00')           # => "3.14"
3.141592.strfnum('0.###')          # => "3.142"
3.140159.strfnum('0.###')          # => "3.14"
3.140159.strfnum('000.00')         # => "003.14"
1234567.strfnum('#,##0')           # => "1,234,567"
1234567.strfnum('#,##0,.0')        # => "1,234.6"
0.56789.strfnum('0.0%')            # => "56.8%"
-12345.strfnum('#,##0')            # => "-12,345"
-12345.strfnum('#,##0;*#,##0')     # => "*12,345"
0.001.strfnum('#,##0;-#,##0;zero') # => "zero"
12345.strfnum('\##,##0')           # => "#12,345"

Customizes Separater Character

Defining following constants, customizes the separator, scaling group size or the percent symbol.

Numeric::DECIMAL_SEPARATOR

(String) A decimal separator. If undefined, uses ".".

Numeric::GROUP_SEPARATOR

(String) A number scaling separator. If undefined, uses ",".

Numeric::GROUP_SIZES

(Integer) Numeric scaling group size. If undefined, uses 3.

Numeric::PERCENT_SYMBOL

(String) A percentage symbol. If undefined, uses "%".

Numeric::DECIMAL_SEPARATOR = ','
Numeric::GROUP_SEPARATOR = ' '

1234567.89.strfnum('#,##0.0')      # => "1 234 567,9"

Parameters:

  • format (String)

    the numeric format string

Returns:

  • (String)

    formated string that is equivalent to this instance



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/util.rb', line 88

def strfnum(format)
  decimal_separator = (defined? ::Numeric::DECIMAL_SEPARATOR) ? ::Numeric::DECIMAL_SEPARATOR : '.'
  group_separator = (defined? ::Numeric::GROUP_SEPARATOR) ? ::Numeric::GROUP_SEPARATOR : ','
  group_sizes = (defined? ::Numeric::GROUP_SIZES) ? ::Numeric::GROUP_SIZES : 3
  percent_symbol = (defined? ::Numeric::PERCENT_SYMBOL) ? ::Numeric::PERCENT_SYMBOL : '%'

  # lexical analysis
  sec = {:int => [], :dec => [], :int_digit => 0, :dec_digit => 0, :place => 0}
  sections = [sec]
  escaped = false
  forcibly = false
  format.split(//).each do |char|
    if escaped
      if sec[:decimal]
        sec[:dec].push(char)
      else
        sec[:int].push(char)
      end
      escaped = false
      next
    end

    case char
    when '0'
      if sec[:decimal]
        sec[:dec_digit] += 1
        sec[:dec].push(:zero)
      else
        forcibly = true
        sec[:int_digit] += 1
        sec[:int].push(:zero)
        if sec[:place] != 0
          sec[:use_separater] = true
          sec[:place] = 0
        end
      end
    when '#'
      if sec[:decimal]
        sec[:dec_digit] += 1
        sec[:dec].push(:sharp)
      else
        sec[:int_digit] += 1
        sec[:int].push(forcibly ? :zero : :sharp)
        if sec[:place] != 0
          sec[:use_separater] = true
          sec[:place] = 0
        end
      end
    when '.'
      sec[:decimal] = true
    when ','
      sec[:place] += group_sizes unless sec[:decimal]
    when ';'
      forcibly = false
      sec = {:int => [], :dec => [], :int_digit => 0, :dec_digit => 0, :place => 0}
      sections.push(sec)
    when '%'
      sec[:percent] = true
      if sec[:decimal]
        sec[:dec].push(percent_symbol)
      else
        sec[:int].push(percent_symbol)
      end
    when '\\'
      escaped = true
    else
      if sec[:decimal]
        sec[:dec].push(char)
      else
        sec[:int].push(char)
      end
    end
  end

  # choosing of the format (fmt)
  need_minus_sign = false
  case sections.size
  when 1
    need_minus_sign = (self < 0)
    frm = sections[0]
  when 2
    if self >= 0
      frm = sections[0]
    else
      frm = sections[1]
    end
  else
    if self > 0
      frm = sections[0]
    elsif self < 0
      frm = sections[1]
    else
      frm = sections[2]
    end
  end

  value = self

  value *= 100 if frm[:percent]
  value = value.quo(10 ** frm[:place]) if frm[:place] != 0

  int_part, dec_part = ("%.#{frm[:dec_digit]}f" % value.abs).split('.')

  if self.nonzero? && int_part == '0' && !(dec_part =~ /[^0]/)
    if sections[2]
      frm = sections[2]
      value = 0
      int_part, dec_part = ("%.#{frm[:dec_digit]}f" % value.abs).split('.')
    elsif self < 0 && sections[1]
      frm = sections[0]
      value = 0
      int_part, dec_part = ("%.#{frm[:dec_digit]}f" % value.abs).split('.')
    end
  end

  # formatting of integer part
  first = 0
  last = int_part.size - frm[:int_digit]
  int_index = frm[:int_digit]
  last_num = nil
  frm[:int].each_with_index do |place_holder, index|
    case place_holder
    when :zero
      if frm[:use_separater]
        if first == last || last < 0
          num = last < 0 ? '0' : int_part[first, 1]
          if last_num && int_index % group_sizes == 0
            frm[:int][index] = group_separator + num
          elsif last_num.nil? && need_minus_sign
            frm[:int][index] = '-' + num
          else
            frm[:int][index] = num
          end
        else
          num = int_part[first..last]
          i = (int_part.size - 1) % 3 + 1
          while i < num.size
            num.insert(i, group_separator)
            i += 4
          end
          frm[:int][index] = last_num.nil? && need_minus_sign ? '-' + num : num
        end
      else
        num = last < 0 ? '0' : int_part[first..last]
        num = '-' + num if last_num.nil? && need_minus_sign
        frm[:int][index] = num
      end
      first = (last += 1)
      int_index -= 1
      last_num = frm[:int][index]
    when :sharp
      if last < 0
        frm[:int][index] = nil
      elsif frm[:use_separater]
        if first == last
          if last_num && int_index % group_sizes == 0
            frm[:int][index] = group_separator + int_part[first, 1]
          elsif last_num.nil? && need_minus_sign
            frm[:int][index] = '-' + int_part[first, 1]
          else
            frm[:int][index] = int_part[first, 1]
          end
        else
          num = int_part[first..last]
          i = (int_part.size - 1) % 3 + 1
          while i < num.size
            num.insert(i, group_separator)
            i += 4
          end
          frm[:int][index] = last_num.nil? && need_minus_sign ? '-' + num : num
        end
      else
        num = int_part[first..last]
        num = '-' + num if last_num.nil? && need_minus_sign
        frm[:int][index] = num
      end
      first = (last += 1)
      int_index -= 1
      last_num = frm[:int][index]
    end
  end

  # formatting of decimal part
  needs_replaceing = false
  needs_decimal_separator = nil
  dec_index = frm[:dec_digit]
  (frm[:dec].size - 1).downto(0) do |index|
    case frm[:dec][index]
    when :zero
      dec_index -= 1
      frm[:dec][index] = dec_part[dec_index, 1]
      needs_replaceing = true
      needs_decimal_separator = decimal_separator
    when :sharp
      dec_index -= 1
      if needs_replaceing || dec_part[dec_index, 1] != '0'
        frm[:dec][index] = dec_part[dec_index, 1]
        needs_replaceing = true
        needs_decimal_separator = decimal_separator
      else
        frm[:dec][index] = nil
      end
    end
  end

  (frm[:int] + [needs_decimal_separator] + frm[:dec]).join
end