nreadable 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +13 -0
- data/Rakefile +9 -0
- data/lib/nreadable.rb +194 -0
- data/test/test_nreadable.rb +98 -0
- metadata +54 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5bb6bbeb440968a93c8c4d6793f2300035f08a24
|
|
4
|
+
data.tar.gz: abdc648a16522cffb06a68c81e46c81bd29da066
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2b4ffb5497654d593406fd3d673de0ff9f35cd6fcdc380c86f2db977ce84e5a4644bc626200d7431d51d54b7b266968e66a843b8456b90d8401c2d4f9b12403d
|
|
7
|
+
data.tar.gz: 276da97917d7135d6815ee93847211c7328357bdb0dac52b749105637af2015b6973a3bb47cdd861d1b7df7bed98542f81bb4980917e263097559a7959fba7c0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Markus Anders
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Rubygem nreadable
|
|
2
|
+
Extends the Numeric Class with the ability to create better readable representations of numeric values as a String.
|
|
3
|
+
|
|
4
|
+
## Features
|
|
5
|
+
- thousands separated numbers ( e.g. 1,000,000 )
|
|
6
|
+
- exponential notation with self-adjusting precision ( e.g. 1e+06 )
|
|
7
|
+
- SI-Prefixes ( e.g. 1000000 = 1M )
|
|
8
|
+
- Binary Prefixes ( e.g. 1048576 = 1Mi )
|
|
9
|
+
- large-number naming system ( e.g. 1 million )
|
|
10
|
+
|
|
11
|
+
## License
|
|
12
|
+
The MIT license. See LICENSE file.
|
|
13
|
+
|
data/Rakefile
ADDED
data/lib/nreadable.rb
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Adds the ability to create various readable representations of numbers to
|
|
2
|
+
# the Numeric Class.
|
|
3
|
+
class Numeric
|
|
4
|
+
|
|
5
|
+
# Length of symbol arrays - 1.
|
|
6
|
+
MAX_NREADABLE_EXP = 8
|
|
7
|
+
|
|
8
|
+
# SI-Prefix symbols for positive exponents.
|
|
9
|
+
SI_PREFIXES_BIG = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
|
10
|
+
|
|
11
|
+
# SI-Prefix symbols for negative exponents.
|
|
12
|
+
SI_PREFIXES_SMALL = ['', 'm', 'µ', 'n', 'p', 'f', 'a', 'z', 'y']
|
|
13
|
+
|
|
14
|
+
# Binary Prefix symbols for positive exponents.
|
|
15
|
+
BINARY_PREFIXES = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']
|
|
16
|
+
|
|
17
|
+
# English short scale names of numeric values. If you need a different
|
|
18
|
+
# scale or language, just replace the content of this array.
|
|
19
|
+
# Make sure it has MAX_NREADABLE_EXP + 1 fields.
|
|
20
|
+
NUMBER_NAMING = ['', 'thousand', 'million', 'billion', 'trillion',
|
|
21
|
+
'quadrillion', 'quintillion', 'sextillion', 'septillion']
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Creates a better readable representation of the number by adding thousands
|
|
25
|
+
# separators.
|
|
26
|
+
# @param delimiter [String] the character(s) to use as the thousands
|
|
27
|
+
# separator.
|
|
28
|
+
# @param separator [String] the character(s) to use as the decimal point.
|
|
29
|
+
# @return [String] the number with thousands separator.
|
|
30
|
+
# @example
|
|
31
|
+
# 12345678.readable #=> "12,345,678"
|
|
32
|
+
|
|
33
|
+
def delimited( delimiter = ',', separator = '.' )
|
|
34
|
+
return self.to_s if self.is_a? Rational
|
|
35
|
+
return self.to_s if self.is_a? Float and !self.finite?
|
|
36
|
+
|
|
37
|
+
negative = self < 0
|
|
38
|
+
result = self.abs.to_s
|
|
39
|
+
length = result.length
|
|
40
|
+
rounds = 0
|
|
41
|
+
decimal_position = result.index('.')
|
|
42
|
+
|
|
43
|
+
pointer = if decimal_position
|
|
44
|
+
result[decimal_position] = separator
|
|
45
|
+
decimal_position - length - 4
|
|
46
|
+
else
|
|
47
|
+
-4
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
while pointer.abs <= length + rounds
|
|
51
|
+
result.insert(pointer, delimiter)
|
|
52
|
+
pointer -= 4
|
|
53
|
+
rounds += 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
result = "-" + result if negative
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Creates a scientific representation of the number.
|
|
62
|
+
# An exponential notation is used if the number is >= 1000 or < 0.01.
|
|
63
|
+
# @param precision [Integer] the maximum precision. Only applies if the
|
|
64
|
+
# exponential notation is used. Must be >= 0.
|
|
65
|
+
# @return [String] the formated number.
|
|
66
|
+
# @example
|
|
67
|
+
# 12.55.scientific #=> "12.55"
|
|
68
|
+
# 10000.scientific #=> "1e+04"
|
|
69
|
+
# 12345.scientific #=> "1.2345e+04"
|
|
70
|
+
|
|
71
|
+
def scientific( precision = 6 )
|
|
72
|
+
raise ArgumentError, "negative precision" if precision < 0
|
|
73
|
+
return self.to_s if (self < 1000 and self >= 0.01) or (self == 0) or
|
|
74
|
+
(self > -1000 and self <= -0.01)
|
|
75
|
+
return self.to_s if self.is_a? Float and !self.finite?
|
|
76
|
+
|
|
77
|
+
result = sprintf("%.#{ precision }e", self)
|
|
78
|
+
position = result.index('e') - 1
|
|
79
|
+
|
|
80
|
+
strip_insignificant_digits( result, position )
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Creates a representation of the number with a SI prefix attached.
|
|
85
|
+
# @param precision [Integer] maximum precision. Must be >= 0.
|
|
86
|
+
# @return [String] the formated number.
|
|
87
|
+
# @example
|
|
88
|
+
# 0.0000012.si_prefixed #=> "1.2µ"
|
|
89
|
+
# 1000.si_prefixed #=> "1k"
|
|
90
|
+
|
|
91
|
+
def si_prefixed( precision = 6 )
|
|
92
|
+
raise ArgumentError, "negative precision" if precision < 0
|
|
93
|
+
return self.to_s if (self < 1000 and self >= 1) or (self == 0) or
|
|
94
|
+
(self > -1000 and self <= -1)
|
|
95
|
+
return self.to_s if self.is_a? Float and !self.finite?
|
|
96
|
+
|
|
97
|
+
number, exponent = base_and_exponent(1000)
|
|
98
|
+
unit = if exponent >= 0
|
|
99
|
+
SI_PREFIXES_BIG[exponent]
|
|
100
|
+
else
|
|
101
|
+
SI_PREFIXES_SMALL[exponent.abs]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
result = nreadable_base_s( number, precision )
|
|
105
|
+
result + unit
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Creates a representation of the number with a binary prefix attached.
|
|
110
|
+
# @param precision [Integer] maximum precision. Must be >= 0.
|
|
111
|
+
# @return [String] the formatted number.
|
|
112
|
+
# @example
|
|
113
|
+
# 1024.bin_prefixed #=> "1Ki"
|
|
114
|
+
# 1_048_576.bin_prefixed + "B" #=> "1MiB"
|
|
115
|
+
|
|
116
|
+
def bin_prefixed( precision = 1 )
|
|
117
|
+
raise ArgumentError, "negative precision" if precision < 0
|
|
118
|
+
return self.to_s if self < 1024 and self > -1024
|
|
119
|
+
return self.to_s if self.is_a? Float and !self.finite?
|
|
120
|
+
|
|
121
|
+
number, exponent = base_and_exponent(1024)
|
|
122
|
+
result = nreadable_base_s( number, precision )
|
|
123
|
+
result + BINARY_PREFIXES[exponent]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Creates a representation of the number using the short scale large-number
|
|
128
|
+
# naming system with english words. You can change this by modifying the
|
|
129
|
+
# NUMBER_NAMING constant.
|
|
130
|
+
# @param precision [Integer] maximum precision. Must be >= 0.
|
|
131
|
+
# @return [String] the formatted number.
|
|
132
|
+
# @example
|
|
133
|
+
# 2000.named #=> "2 tousand"
|
|
134
|
+
# 2_310_000.named #=> "2.3 million"
|
|
135
|
+
|
|
136
|
+
def named( precision = 1 )
|
|
137
|
+
raise ArgumentError, "negative precision" if precision < 0
|
|
138
|
+
return self.to_s if self < 1000 and self > -1000
|
|
139
|
+
return self.to_s if self.is_a? Float and !self.finite?
|
|
140
|
+
|
|
141
|
+
number, exponent = base_and_exponent(1000)
|
|
142
|
+
result = nreadable_base_s( number, precision )
|
|
143
|
+
"#{result} #{NUMBER_NAMING[exponent]}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
# calculates the base and the exponent of self and returns it in an array.
|
|
149
|
+
# @param base [Integer] base to use in the calculation.
|
|
150
|
+
def base_and_exponent( base )
|
|
151
|
+
return [0, 0] if self == 0
|
|
152
|
+
|
|
153
|
+
f = self.to_f
|
|
154
|
+
exponent = (Math.log(f.abs) / Math.log(base)).floor
|
|
155
|
+
if exponent > MAX_NREADABLE_EXP
|
|
156
|
+
exponent = MAX_NREADABLE_EXP
|
|
157
|
+
number = self / (base ** exponent)
|
|
158
|
+
elsif exponent < -MAX_NREADABLE_EXP
|
|
159
|
+
exponent = -MAX_NREADABLE_EXP
|
|
160
|
+
number = self / (base ** exponent)
|
|
161
|
+
else
|
|
162
|
+
number = (f / (base ** exponent)).round(8) #round to counter float errors
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
[number, exponent]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# converts number to string with given precision and thousands separator.
|
|
169
|
+
def nreadable_base_s( number, precision )
|
|
170
|
+
number = number.round(precision) if number.is_a? Float
|
|
171
|
+
result = number.delimited
|
|
172
|
+
strip_insignificant_digits(result) if number.is_a? Float
|
|
173
|
+
|
|
174
|
+
result
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# strips trailing zeros and the . if it's the last character
|
|
178
|
+
def strip_insignificant_digits( string, index = nil )
|
|
179
|
+
index = string.length - 1 if index.nil?
|
|
180
|
+
|
|
181
|
+
while string[index] == '0'
|
|
182
|
+
string[index] = ''
|
|
183
|
+
index -= 1
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
if string[index] == '.'
|
|
187
|
+
string[index] = ''
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
string
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
end
|
|
194
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'nreadable'
|
|
3
|
+
|
|
4
|
+
class NreadableTest < Test::Unit::TestCase
|
|
5
|
+
def test_delimited_int
|
|
6
|
+
assert_equal "0", 0.delimited
|
|
7
|
+
assert_equal "999", 999.delimited
|
|
8
|
+
assert_equal "1,234,567", 1234567.delimited
|
|
9
|
+
assert_equal "-1,000", -1000.delimited
|
|
10
|
+
assert_equal "-100", -100.delimited
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_delimited_float
|
|
14
|
+
assert_equal "1.234", 1.234.delimited
|
|
15
|
+
assert_equal "1,234.5678", 1234.5678.delimited
|
|
16
|
+
assert_equal "-1,000.1", -1000.1.delimited
|
|
17
|
+
assert_equal "Infinity", Float::INFINITY.delimited
|
|
18
|
+
assert_equal "-Infinity", (-Float::INFINITY).delimited
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_scientific_int
|
|
22
|
+
assert_equal "0", 0.scientific
|
|
23
|
+
assert_equal "1.234567e+06", 1234567.scientific
|
|
24
|
+
assert_equal "-1.234567e+06", -1234567.scientific
|
|
25
|
+
assert_equal "999", 999.scientific
|
|
26
|
+
assert_equal "1e+03", 1000.scientific
|
|
27
|
+
assert_equal "1.1e+03", 1100.scientific
|
|
28
|
+
assert_equal "-999", -999.scientific
|
|
29
|
+
assert_equal "-1e+03", -1000.scientific
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_scientific_float
|
|
33
|
+
assert_equal "0.01", 0.01.scientific
|
|
34
|
+
assert_equal "9.11e-03", 0.00911.scientific
|
|
35
|
+
assert_equal "-0.01", -0.01.scientific
|
|
36
|
+
assert_equal "-9.11e-03", -0.00911.scientific
|
|
37
|
+
assert_equal "1.234567e+03", 1234.567.scientific
|
|
38
|
+
assert_equal "-1.234567e+03", -1234.567.scientific
|
|
39
|
+
assert_equal "Infinity", Float::INFINITY.scientific
|
|
40
|
+
assert_equal "-Infinity", (-Float::INFINITY).scientific
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_si_prefixed_multi
|
|
44
|
+
assert_equal "0", 0.si_prefixed
|
|
45
|
+
assert_equal "999.999", 999.999.si_prefixed
|
|
46
|
+
assert_equal "1k", 1000.si_prefixed
|
|
47
|
+
assert_equal "1.23k", 1230.si_prefixed
|
|
48
|
+
assert_equal "-999.999", -999.999.si_prefixed
|
|
49
|
+
assert_equal "-1k", -1000.si_prefixed
|
|
50
|
+
assert_equal "1Y", (10**24).si_prefixed
|
|
51
|
+
assert_equal "1,000Y", (10**27).si_prefixed
|
|
52
|
+
assert_equal "Infinity", Float::INFINITY.si_prefixed
|
|
53
|
+
assert_equal "-Infinity", (-Float::INFINITY).si_prefixed
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_si_prefixed_fraction
|
|
57
|
+
assert_equal "10m", 0.01.si_prefixed
|
|
58
|
+
assert_equal "-10m", -0.01.si_prefixed
|
|
59
|
+
assert_equal "1m", 0.001.si_prefixed
|
|
60
|
+
assert_equal "100µ", 0.0001.si_prefixed
|
|
61
|
+
assert_equal "-100.1µ", -0.0001001.si_prefixed
|
|
62
|
+
assert_equal "1y", (10**-24).si_prefixed
|
|
63
|
+
assert_equal "1/10y", (10**-25).si_prefixed
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_bin_prefixed
|
|
67
|
+
assert_equal "0", 0.bin_prefixed
|
|
68
|
+
assert_equal "1023.999", 1023.999.bin_prefixed
|
|
69
|
+
assert_equal "1Ki", 1024.bin_prefixed
|
|
70
|
+
assert_equal "-1023.999", -1023.999.bin_prefixed
|
|
71
|
+
assert_equal "-1Ki", -1024.bin_prefixed
|
|
72
|
+
assert_equal "Infinity", Float::INFINITY.bin_prefixed
|
|
73
|
+
assert_equal "-Infinity", (-Float::INFINITY).bin_prefixed
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_named_int
|
|
77
|
+
assert_equal "0", 0.named
|
|
78
|
+
assert_equal "999", 999.named
|
|
79
|
+
assert_equal "1 thousand", 1000.named
|
|
80
|
+
assert_equal "2 thousand", 1990.named
|
|
81
|
+
assert_equal "-999", -999.named
|
|
82
|
+
assert_equal "-1 thousand", -1000.named
|
|
83
|
+
assert_equal "3.4 million", 3_430_000.named
|
|
84
|
+
assert_equal "1 septillion", (10**24).named
|
|
85
|
+
assert_equal "1,000 septillion", (10**27).named
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_named_float
|
|
89
|
+
assert_equal "0.5", 0.5.named
|
|
90
|
+
assert_equal "0.004", 0.004.named
|
|
91
|
+
assert_equal "999.999", 999.999.named
|
|
92
|
+
assert_equal "2 thousand", 1999.99.named
|
|
93
|
+
assert_equal "1.23456 thousand", 1234.56.named(5)
|
|
94
|
+
assert_equal "Infinity", Float::INFINITY.named
|
|
95
|
+
assert_equal "-Infinity", (-Float::INFINITY).named
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
metadata
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nreadable
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Markus Anders
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-09-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
Extends the Numeric Class with the ability to create better readable
|
|
15
|
+
representations of numeric values as a String.
|
|
16
|
+
In the current version it supports thousands separated numbers,
|
|
17
|
+
an exponential notation with self-adjusting precision, SI-Prefixes,
|
|
18
|
+
Binary Prefixes and a large-number naming system.
|
|
19
|
+
email:
|
|
20
|
+
executables: []
|
|
21
|
+
extensions: []
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
files:
|
|
24
|
+
- LICENSE
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- lib/nreadable.rb
|
|
28
|
+
- test/test_nreadable.rb
|
|
29
|
+
homepage: http://github.com/wups/nreadable
|
|
30
|
+
licenses:
|
|
31
|
+
- MIT
|
|
32
|
+
metadata: {}
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubyforge_project:
|
|
49
|
+
rubygems_version: 2.2.2
|
|
50
|
+
signing_key:
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: Create various readable representations of numeric values.
|
|
53
|
+
test_files: []
|
|
54
|
+
has_rdoc:
|