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 }