1 module bc..string.numeric;
2
3 import bc.core.intrinsics;
4 import std.traits : isIntegral, isSigned;
5
6 pure @safe nothrow @nogc:
7
8 /// Calculates number of digits of the provided number including sign character for negative numbers.
9 int numDigits(T)(T number)
10 if (isIntegral!T)
11 {
12 pragma(inline);
13
14 // handle negative numbers
15 static if (isSigned!T)
16 {
17 if (_expect(number == long.min, false)) return 20; // for safety as it can't be converted to positive number
18 if (number < 0) return 1 + numDigits(-long(number));
19 }
20
21 if (__ctfe)
22 {
23 // simple ctfe version
24 import std.traits : Unqual;
25 if (_expect(!number, false)) return 1; // special case for 0
26 int digits = 0;
27 alias UT = Unqual!T;
28 UT n = number;
29 while (n) { n /= 10; digits++; }
30 return digits;
31 }
32 else
33 {
34 // specialization for 1B numbers
35 static if (T.sizeof == 1)
36 {
37 static immutable ubyte[256] dmap = () {
38 ubyte[256] ret;
39 for (int i=0; i < 256; ++i) ret[i] = cast(ubyte)numDigits(i);
40 return ret;
41 }();
42 return dmap[number];
43 }
44 else
45 {
46 if (_expect(number >= 10_000, false)) {
47 if (number >= 10_000_000) {
48 if (number >= 100_000_000) {
49 if (number >= 1_000_000_000) {
50 static if (T.sizeof <= 4) return 10;
51 else {
52 // 8bit number branch
53 if (number >= 10_000_000_000) {
54 if (number >= 100_000_000_000) {
55 if (number >= 1_000_000_000_000) {
56 if (number >= 10_000_000_000_000) {
57 if (number >= 100_000_000_000_000) {
58 if (number >= 1_000_000_000_000_000) {
59 if (number >= 10_000_000_000_000_000) {
60 if (number >= 100_000_000_000_000_000) {
61 if (number >= 1_000_000_000_000_000_000) {
62 if (number >= 10_000_000_000_000_000_000UL)
63 return 20;
64 return 19;
65 }
66 return 18;
67 }
68 return 17;
69 }
70 return 16;
71 }
72 return 15;
73 }
74 return 14;
75 }
76 return 13;
77 }
78 return 12;
79 }
80 return 11;
81 }
82 return 10;
83 }
84 }
85 return 9;
86 }
87 return 8;
88 }
89 if (number >= 100_000) {
90 if (number >= 1_000_000) return 7;
91 return 6;
92 }
93 return 5;
94 }
95 if (number >= 100) {
96 if (number >= 1_000) return 4;
97 return 3;
98 }
99 if (number >= 10) return 2;
100 return 1;
101 }
102 }
103 }
104
105 ///
106 @("numDigits CT")
107 @safe unittest
108 {
109 // ctfe tests
110 static assert(numDigits(0) == 1);
111 static assert(numDigits(11) == 2);
112 static assert(numDigits(-1) == 2);
113
114 static assert(numDigits(int.min) == 11);
115 static assert(numDigits(int.max) == 10);
116 static assert(numDigits(long.min) == 20);
117 static assert(numDigits(long.max) == 19);
118 static assert(numDigits(ulong.min) == 1);
119 static assert(numDigits(ulong.max) == 20);
120 }
121
122 ///
123 @("numDigits RT")
124 @safe unittest
125 {
126 // uint.max = 4_294_967_295 -> 10 digits
127 // ulong.max = 18_446_744_073_709_551_615 -> 20 digits
128 // long max = 9_223_372_036_854_775_807 -> 19 digits
129
130 // rt tests
131 assert(numDigits(0) == 1);
132 assert(numDigits(11) == 2);
133 assert(numDigits(-1) == 2);
134 assert(numDigits(-123) == 4);
135
136 assert(numDigits(int.min) == 11);
137 assert(numDigits(int.max) == 10);
138 assert(numDigits(long.min) == 20);
139 assert(numDigits(long.max) == 19);
140 assert(numDigits(ulong.min) == 1);
141 assert(numDigits(ulong.max) == 20);
142
143 long n = 10;
144 foreach (i; 0..20)
145 {
146 import std.math : pow;
147 assert(numDigits(pow(10UL, i)) == i+1);
148 }
149 }