1 /******************************************************************************
2
3 Converts values into a metric representation with a scaled mantissa and a
4 decimal exponent unit prefix character.
5
6 Copyright:
7 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8 All rights reserved.
9
10 License:
11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12 Alternatively, this file may be distributed under the terms of the Tango
13 3-Clause BSD License (see LICENSE_BSD.txt for details).
14
15 ******************************************************************************/
16
17 module ocean.text.util.MetricPrefix;
18
19
20
21
22 import core.stdc.math;
23
24 import ocean.core.Verify;
25
26
27 /*******************************************************************************
28
29 Metric prefix struct.
30
31 Usage example:
32
33 ---
34
35 import ocean.text.util.MetricPrefix;
36
37 // Number to display in metric prefixed mode.
38 const number = 2876873683;
39
40 // Struct instance
41 MetricPrefix metric;
42
43 // Calculate the binary metric prefix of the number.
44 metric.bin(number);
45
46 // Output metric prefixed string (2.679297405Gb, in this case).
47 Stdout.formatln("{}{}b", metric.scaled, metric.prefix);
48
49 ---
50
51 *******************************************************************************/
52
53 public struct MetricPrefix
54 {
55 /**************************************************************************
56
57 Scaled mantissa; set by bin()/dec()
58
59 **************************************************************************/
60
61 float scaled = 0.;
62
63 /**************************************************************************
64
65 Metric decimal power unit prefix; set by bin()/dec()
66
67 **************************************************************************/
68
69 dchar prefix = ' ';
70
71 public enum BinaryPrefixes = [' ', 'K', 'M', 'G', 'T', 'P', 'E'];
72
73 /**************************************************************************
74
75 Converts n into a metric-like prefixed representation, using powers of
76 1024.
77 Example: For n == 12345678 this.scaled about 11.78 and this.prefix is
78 'M'.
79
80 Params:
81 n = number to convert
82
83 Returns:
84 this instance
85
86 **************************************************************************/
87
88 typeof(&this) bin ( T : float ) ( T n )
89 {
90 this.scaled = n;
91
92 int i;
93
94 static if (is (T : long))
95 {
96 for (i = 0; (n > 0x400) && (i < BinaryPrefixes.length); i++)
97 {
98 n >>= 10;
99 }
100 }
101 else
102 {
103 frexpf(n, &i);
104 i /= 10;
105 }
106
107 this.scaled = ldexpf(this.scaled, i * -10);
108
109 this.prefix = BinaryPrefixes[i];
110
111 return &this;
112 }
113
114 public enum DecimalPrefixes = [cast(wchar)'p', 'n', 'µ', 'm', ' ', 'k', 'M', 'G', 'T'];
115
116 /**************************************************************************
117
118 Converts n into a metric prefixed representation.
119 Example: For n == 12345678 this.scaled is about 12.35 and this.prefix is
120 'M'.
121
122 Params:
123 n = number to convert
124 e = input prefix: 0 = None, 1 = 'k', -1 = 'm', 2 = 'M', -2 = 'µ' etc.,
125 up to +/- 4
126
127 Returns:
128 this instance
129
130 **************************************************************************/
131
132 typeof(&this) dec ( T : float ) ( T n, int e = 0 )
133 {
134 verify (-5 < e && e < 5);
135
136 this.scaled = n;
137
138 int i = 4;
139
140 if (n != 0)
141 {
142 if (n > 1)
143 {
144 for (i += e; (n > 1000) && (i+1 < DecimalPrefixes.length); i++)
145 {
146 n /= 1000;
147 this.scaled /= 1000;
148 }
149 }
150 else
151 {
152 for (i += e; (n < 1) && (i-1 > 0); i--)
153 {
154 n *= 1000;
155 this.scaled *= 1000;
156 }
157 }
158 }
159
160 this.prefix = DecimalPrefixes[i];
161
162 return &this;
163 }
164 }
165
166
167
168 /*******************************************************************************
169
170 Splits the given number by binary prefixes (K, M, T, etc), passing the
171 prefix character, the prefix order and the count per prefix to the output
172 delegate.
173
174 Params:
175 n = number to split
176 output_dg = delegate which receives prefix values
177
178 See also: BitGrouping in ocean.text.util.DigitGrouping, for a method which
179 automatically formats a split binary prefix string.
180
181 Note that if n == 0, the output delegate will not be called.
182
183 Usage example:
184
185 ---
186
187 import ocean.text.util.MetricPrefix;
188
189 // Number to split by binary prefix.
190 const number = 2876873683;
191
192 // Delegate which receives the split info.
193 void split ( char prefix, uint order, ulong order_val )
194 {
195 Stdout.formatln("Order {}: {}{}", order, order_val, prefix);
196 }
197
198 // Perform the split.
199 splitBinaryPrefix(number, &split);
200
201 ---
202
203 *******************************************************************************/
204
205 public void splitBinaryPrefix ( ulong n, scope void delegate ( char prefix, uint order, ulong order_val ) output_dg )
206 {
207 auto length = MetricPrefix.BinaryPrefixes.length;
208 verify (length < int.max);
209 for ( int order = cast(int) length - 1; order >= 0; order-- )
210 {
211 auto shift = order * 10;
212
213 ulong mask = 0x3ff;
214 mask <<= shift;
215
216 auto order_val = (n & mask) >> shift;
217
218 output_dg(MetricPrefix.BinaryPrefixes[order], order, order_val);
219 }
220 }