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 }