Làm cách nào tôi có thể đếm tổng số chữ số của một số trong C #? Ví dụ, số 887979789 có 9 chữ số.
Làm cách nào tôi có thể đếm tổng số chữ số của một số trong C #? Ví dụ, số 887979789 có 9 chữ số.
Câu trả lời:
Nếu không chuyển đổi thành một chuỗi, bạn có thể thử:
Math.Ceiling(Math.Log10(n));
Chỉnh sửa sau nhận xét của ysap:
Math.Floor(Math.Log10(n) + 1);
n
là 0
có thể chỉ trả về 1
:) Quá xử lý các giá trị âm chỉ cần thay thế n
bằng Math.Abs(n)
.
Thử cái này:
myint.ToString().Length
Điều đó có hiệu quả không?
int
, vì vậy tôi cho rằng đó không phải là vấn đề.)
Bất kỳ phương thức mở rộng nào sau đây sẽ thực hiện công việc. Tất cả chúng đều coi dấu trừ là một chữ số và hoạt động chính xác với tất cả các giá trị đầu vào có thể có. Chúng cũng hoạt động cho .NET Framework và .NET Core. Tuy nhiên, có sự khác biệt về hiệu suất có liên quan (được thảo luận bên dưới), tùy thuộc vào lựa chọn Nền tảng / Khung của bạn.
Phiên bản Int32:
public static class Int32Extensions
{
// IF-CHAIN:
public static int Digits_IfChain(this int n)
{
if (n >= 0)
{
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
else
{
if (n > -10) return 2;
if (n > -100) return 3;
if (n > -1000) return 4;
if (n > -10000) return 5;
if (n > -100000) return 6;
if (n > -1000000) return 7;
if (n > -10000000) return 8;
if (n > -100000000) return 9;
if (n > -1000000000) return 10;
return 11;
}
}
// USING LOG10:
public static int Digits_Log10(this int n) =>
n == 0 ? 1 : (n > 0 ? 1 : 2) + (int)Math.Log10(Math.Abs((double)n));
// WHILE LOOP:
public static int Digits_While(this int n)
{
int digits = n < 0 ? 2 : 1;
while ((n /= 10) != 0) ++digits;
return digits;
}
// STRING CONVERSION:
public static int Digits_String(this int n) =>
n.ToString().Length;
}
Phiên bản Int64:
public static class Int64Extensions
{
// IF-CHAIN:
public static int Digits_IfChain(this long n)
{
if (n >= 0)
{
if (n < 10L) return 1;
if (n < 100L) return 2;
if (n < 1000L) return 3;
if (n < 10000L) return 4;
if (n < 100000L) return 5;
if (n < 1000000L) return 6;
if (n < 10000000L) return 7;
if (n < 100000000L) return 8;
if (n < 1000000000L) return 9;
if (n < 10000000000L) return 10;
if (n < 100000000000L) return 11;
if (n < 1000000000000L) return 12;
if (n < 10000000000000L) return 13;
if (n < 100000000000000L) return 14;
if (n < 1000000000000000L) return 15;
if (n < 10000000000000000L) return 16;
if (n < 100000000000000000L) return 17;
if (n < 1000000000000000000L) return 18;
return 19;
}
else
{
if (n > -10L) return 2;
if (n > -100L) return 3;
if (n > -1000L) return 4;
if (n > -10000L) return 5;
if (n > -100000L) return 6;
if (n > -1000000L) return 7;
if (n > -10000000L) return 8;
if (n > -100000000L) return 9;
if (n > -1000000000L) return 10;
if (n > -10000000000L) return 11;
if (n > -100000000000L) return 12;
if (n > -1000000000000L) return 13;
if (n > -10000000000000L) return 14;
if (n > -100000000000000L) return 15;
if (n > -1000000000000000L) return 16;
if (n > -10000000000000000L) return 17;
if (n > -100000000000000000L) return 18;
if (n > -1000000000000000000L) return 19;
return 20;
}
}
// USING LOG10:
public static int Digits_Log10(this long n) =>
n == 0L ? 1 : (n > 0L ? 1 : 2) + (int)Math.Log10(Math.Abs((double)n));
// WHILE LOOP:
public static int Digits_While(this long n)
{
int digits = n < 0 ? 2 : 1;
while ((n /= 10L) != 0L) ++digits;
return digits;
}
// STRING CONVERSION:
public static int Digits_String(this long n) =>
n.ToString().Length;
}
Câu trả lời này bao gồm các bài kiểm tra được thực hiện cho cả hai loại Int32
và Int64
các loại, sử dụng một mảng / số được 100.000.000
lấy mẫu ngẫu nhiên . Tập dữ liệu ngẫu nhiên được xử lý trước thành một mảng trước khi thực hiện các bài kiểm tra.int
long
Kiểm tra tính nhất quán trong 4 phương pháp khác nhau cũng đã được thực hiện, cho MinValue
, trường hợp biên giới tiêu cực, -1
, 0
, 1
, trường hợp biên giới tích cực, MaxValue
và cũng cho cả dữ liệu ngẫu nhiên. Không có thử nghiệm nhất quán nào không thành công đối với các phương pháp được cung cấp ở trên, NGOẠI TRỪ cho phương pháp LOG10 (điều này sẽ được thảo luận sau).
Các thử nghiệm được thực hiện trên .NET Framework 4.7.2
và .NET Core 2.2
; cho x86
và x64
nền tảng, trên máy Bộ xử lý Intel 64-bit, với Windows 10
và với VS2017 v.15.9.17
. 4 trường hợp sau đây có cùng ảnh hưởng đến kết quả hoạt động:
.NET Framework (x86)
Platform = x86
Platform = AnyCPU
, Prefer 32-bit
được chọn trong cài đặt dự án
.NET Framework (x64)
Platform = x64
Platform = AnyCPU
, Prefer 32-bit
không được chọn trong cài đặt dự án
.NET Core (x86)
"C:\Program Files (x86)\dotnet\dotnet.exe" bin\Release\netcoreapp2.2\ConsoleApp.dll
"C:\Program Files (x86)\dotnet\dotnet.exe" bin\x86\Release\netcoreapp2.2\ConsoleApp.dll
.NET Core (x64)
"C:\Program Files\dotnet\dotnet.exe" bin\Release\netcoreapp2.2\ConsoleApp.dll
"C:\Program Files\dotnet\dotnet.exe" bin\x64\Release\netcoreapp2.2\ConsoleApp.dll
Các bài kiểm tra hiệu suất dưới đây tạo ra sự phân bố đồng đều các giá trị trong phạm vi rộng các giá trị mà một số nguyên có thể giả định. Điều này có nghĩa là có nhiều cơ hội kiểm tra các giá trị với số lượng lớn các chữ số. Trong các tình huống thực tế, hầu hết các giá trị có thể nhỏ, vì vậy IF-CHAIN sẽ hoạt động tốt hơn nữa. Hơn nữa, bộ xử lý sẽ lưu vào bộ nhớ cache và tối ưu hóa các quyết định IF-CHAIN theo tập dữ liệu của bạn.
Như @AlanSingfield đã chỉ ra trong phần nhận xét, phương thức LOG10 phải được sửa bằng cách ép kiểu vào double
bên trong Math.Abs()
đối với trường hợp giá trị đầu vào là int.MinValue
hoặc long.MinValue
.
Về các bài kiểm tra hiệu suất ban đầu mà tôi đã thực hiện trước khi chỉnh sửa câu hỏi này (nó đã phải được chỉnh sửa hàng triệu lần rồi), có một trường hợp cụ thể được chỉ ra bởi @ GyörgyKőszeg , trong đó phương pháp IF-CHAIN hoạt động chậm hơn phương pháp LOG10.
Điều này vẫn xảy ra, mặc dù mức độ khác biệt đã giảm đi nhiều sau khi khắc phục sự cố được chỉ ra bởi @AlanSingfield . Bản sửa lỗi này (thêm một kiểu vào double
) gây ra lỗi tính toán khi giá trị đầu vào là chính xác -999999999999999999
: phương thức LOG10 trả về 20
thay vì 19
. Phương thức LOG10 cũng phải có bộ if
phận bảo vệ trong trường hợp giá trị đầu vào bằng 0.
Phương thức LOG10 khá phức tạp để làm việc cho tất cả các giá trị, có nghĩa là bạn nên tránh nó. Nếu ai đó tìm ra cách làm cho nó hoạt động chính xác cho tất cả các bài kiểm tra tính nhất quán bên dưới, vui lòng gửi nhận xét!
Phương thức WHILE cũng có phiên bản được cấu trúc lại gần đây nhanh hơn, nhưng nó vẫn còn chậm Platform = x86
(cho đến bây giờ tôi không thể tìm ra lý do tại sao).
Phương thức STRING luôn chậm: nó phân bổ quá nhiều bộ nhớ mà không có gì. Điều thú vị là trong .NET Core, phân bổ chuỗi dường như nhanh hơn nhiều so với .NET Framework. Tốt để biết.
Phương pháp IF-CHAIN sẽ hoạt động tốt hơn tất cả các phương pháp khác trong 99,99% trường hợp; và, theo ý kiến cá nhân của tôi, là lựa chọn tốt nhất của bạn (xem xét tất cả các điều chỉnh cần thiết để làm cho phương pháp LOG10 hoạt động chính xác và hiệu suất kém của hai phương pháp còn lại).
Cuối cùng, kết quả là:
Vì các kết quả này phụ thuộc vào phần cứng, tôi khuyên bạn nên chạy các bài kiểm tra hiệu suất bên dưới trên máy tính của chính mình nếu bạn thực sự cần chắc chắn 100% trong trường hợp cụ thể của mình.
Dưới đây là mã cho kiểm tra hiệu suất và kiểm tra tính nhất quán. Mã giống nhau được sử dụng cho cả .NET Framework và .NET Core.
using System;
using System.Diagnostics;
namespace NumberOfDigits
{
// Performance Tests:
class Program
{
private static void Main(string[] args)
{
Console.WriteLine("\r\n.NET Core");
RunTests_Int32();
RunTests_Int64();
}
// Int32 Performance Tests:
private static void RunTests_Int32()
{
Console.WriteLine("\r\nInt32");
const int size = 100000000;
int[] samples = new int[size];
Random random = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < size; ++i)
samples[i] = random.Next(int.MinValue, int.MaxValue);
Stopwatch sw1 = new Stopwatch();
sw1.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_IfChain();
sw1.Stop();
Console.WriteLine($"IfChain: {sw1.ElapsedMilliseconds} ms");
Stopwatch sw2 = new Stopwatch();
sw2.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_Log10();
sw2.Stop();
Console.WriteLine($"Log10: {sw2.ElapsedMilliseconds} ms");
Stopwatch sw3 = new Stopwatch();
sw3.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_While();
sw3.Stop();
Console.WriteLine($"While: {sw3.ElapsedMilliseconds} ms");
Stopwatch sw4 = new Stopwatch();
sw4.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_String();
sw4.Stop();
Console.WriteLine($"String: {sw4.ElapsedMilliseconds} ms");
// Start of consistency tests:
Console.WriteLine("Running consistency tests...");
bool isConsistent = true;
// Consistency test on random set:
for (int i = 0; i < samples.Length; ++i)
{
int s = samples[i];
int a = s.Digits_IfChain();
int b = s.Digits_Log10();
int c = s.Digits_While();
int d = s.Digits_String();
if (a != b || c != d || a != c)
{
Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
isConsistent = false;
break;
}
}
// Consistency test of special values:
samples = new int[]
{
0,
int.MinValue, -1000000000, -999999999, -100000000, -99999999, -10000000, -9999999, -1000000, -999999, -100000, -99999, -10000, -9999, -1000, -999, -100, -99, -10, -9, - 1,
int.MaxValue, 1000000000, 999999999, 100000000, 99999999, 10000000, 9999999, 1000000, 999999, 100000, 99999, 10000, 9999, 1000, 999, 100, 99, 10, 9, 1,
};
for (int i = 0; i < samples.Length; ++i)
{
int s = samples[i];
int a = s.Digits_IfChain();
int b = s.Digits_Log10();
int c = s.Digits_While();
int d = s.Digits_String();
if (a != b || c != d || a != c)
{
Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
isConsistent = false;
break;
}
}
// Consistency test result:
if (isConsistent)
Console.WriteLine("Consistency tests are OK");
}
// Int64 Performance Tests:
private static void RunTests_Int64()
{
Console.WriteLine("\r\nInt64");
const int size = 100000000;
long[] samples = new long[size];
Random random = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < size; ++i)
samples[i] = Math.Sign(random.Next(-1, 1)) * (long)(random.NextDouble() * long.MaxValue);
Stopwatch sw1 = new Stopwatch();
sw1.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_IfChain();
sw1.Stop();
Console.WriteLine($"IfChain: {sw1.ElapsedMilliseconds} ms");
Stopwatch sw2 = new Stopwatch();
sw2.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_Log10();
sw2.Stop();
Console.WriteLine($"Log10: {sw2.ElapsedMilliseconds} ms");
Stopwatch sw3 = new Stopwatch();
sw3.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_While();
sw3.Stop();
Console.WriteLine($"While: {sw3.ElapsedMilliseconds} ms");
Stopwatch sw4 = new Stopwatch();
sw4.Start();
for (int i = 0; i < size; ++i) samples[i].Digits_String();
sw4.Stop();
Console.WriteLine($"String: {sw4.ElapsedMilliseconds} ms");
// Start of consistency tests:
Console.WriteLine("Running consistency tests...");
bool isConsistent = true;
// Consistency test on random set:
for (int i = 0; i < samples.Length; ++i)
{
long s = samples[i];
int a = s.Digits_IfChain();
int b = s.Digits_Log10();
int c = s.Digits_While();
int d = s.Digits_String();
if (a != b || c != d || a != c)
{
Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
isConsistent = false;
break;
}
}
// Consistency test of special values:
samples = new long[]
{
0,
long.MinValue, -1000000000000000000, -999999999999999999, -100000000000000000, -99999999999999999, -10000000000000000, -9999999999999999, -1000000000000000, -999999999999999, -100000000000000, -99999999999999, -10000000000000, -9999999999999, -1000000000000, -999999999999, -100000000000, -99999999999, -10000000000, -9999999999, -1000000000, -999999999, -100000000, -99999999, -10000000, -9999999, -1000000, -999999, -100000, -99999, -10000, -9999, -1000, -999, -100, -99, -10, -9, - 1,
long.MaxValue, 1000000000000000000, 999999999999999999, 100000000000000000, 99999999999999999, 10000000000000000, 9999999999999999, 1000000000000000, 999999999999999, 100000000000000, 99999999999999, 10000000000000, 9999999999999, 1000000000000, 999999999999, 100000000000, 99999999999, 10000000000, 9999999999, 1000000000, 999999999, 100000000, 99999999, 10000000, 9999999, 1000000, 999999, 100000, 99999, 10000, 9999, 1000, 999, 100, 99, 10, 9, 1,
};
for (int i = 0; i < samples.Length; ++i)
{
long s = samples[i];
int a = s.Digits_IfChain();
int b = s.Digits_Log10();
int c = s.Digits_While();
int d = s.Digits_String();
if (a != b || c != d || a != c)
{
Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
isConsistent = false;
break;
}
}
// Consistency test result:
if (isConsistent)
Console.WriteLine("Consistency tests are OK");
}
}
}
long.MaxValue
Log10 thì tốt hơn đáng kể. Hay chỉ là trong .NET Core?
Int32
và Int64
tạo các bộ dữ liệu khác nhau, có thể giải thích tại sao Int64
nhanh hơn Int32
trong một số trường hợp. Mặc dù, trong Int32
thử nghiệm và trong Int64
thử nghiệm, các bộ dữ liệu không bị thay đổi khi thử nghiệm các phương pháp tính toán khác nhau. Bây giờ liên quan đến .NET Core, tôi nghi ngờ rằng có bất kỳ tối ưu hóa kỳ diệu nào trong thư viện Math sẽ thay đổi những kết quả này, nhưng tôi rất muốn nghe thêm về điều đó (câu trả lời của tôi đã rất lớn, có lẽ là một trong những điều lớn nhất trong SO ;-)
for
vòng qua enumerations
, tôi trước quá trình tập hợp dữ liệu ngẫu nhiên, và tránh việc sử dụng Generics, Tasks, Function<>
, Action<>
, hoặc bất kỳ khuôn khổ đo đen đóng hộp). Tóm lại, hãy giữ nó đơn giản. Tôi cũng giết tất cả các ứng dụng không cần thiết (Skype, Windows Defender, vô hiệu hóa Anti-Virus, Chrome, Microsoft Office cache, v.v.).
Không trực tiếp C #, nhưng công thức là: n = floor(log10(x)+1)
log10
trong hầu hết các trường hợp là một hàm thư viện. Tại sao bạn muốn tự mình thực hiện và bạn gặp phải những vấn đề gì? log10(x) = log2(x) / log2(10)
, hoặc nói chung logA(x) = logB(x) / logB(A)
.
Log10(0)
là -infinity. Log10 không thể được sử dụng để tính toán số chữ số của số âm trừ khi bạn sử dụng Math.Abs()
trước khi chuyển giá trị cho Log10. Nhưng sau đó Math.Abs(int.MinValue)
ném một Ngoại lệ ( long.MinValue
cũng như trong trường hợp Int64). Nếu chúng ta ép số lên gấp đôi trước khi chuyển nó đến Log10, thì nó hoạt động với hầu hết tất cả các số ngoại trừ -999999999999999999
(trong trường hợp Int64). Bạn có biết bất kỳ công thức nào để tính số chữ số sử dụng log10 và chấp nhận bất kỳ giá trị int32 hoặc int64 nào làm đầu vào và đầu ra chỉ các giá trị hợp lệ không?
Các câu trả lời đã có ở đây hoạt động đối với số nguyên không dấu, nhưng tôi chưa tìm thấy giải pháp tốt để nhận số chữ số từ số thập phân và nhân đôi.
public static int Length(double number)
{
number = Math.Abs(number);
int length = 1;
while ((number /= 10) >= 1)
length++;
return length;
}
//number of digits in 0 = 1,
//number of digits in 22.1 = 2,
//number of digits in -23 = 2
Bạn có thể thay đổi loại đầu vào từ double
để decimal
nếu chính xác vấn đề, nhưng số thập phân có một giới hạn quá.
Câu trả lời của Steve là đúng , nhưng nó không hiệu quả với các số nguyên nhỏ hơn 1.
Đây là một phiên bản cập nhật hoạt động cho phủ định:
int digits = n == 0 ? 1 : Math.Floor(Math.Log10(Math.Abs(n)) + 1)
digits = n == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(n)) + 1);
n = int.MinValue
.
Sử dụng đệ quy (đôi khi được hỏi khi phỏng vấn)
public int CountDigits(int number)
{
// In case of negative numbers
number = Math.Abs(number);
if (number >= 10)
return CountDigits(number / 10) + 1;
return 1;
}
number = int.MinValue
.
Đây là cách triển khai sử dụng tìm kiếm nhị phân. Có vẻ là nhanh nhất cho đến nay trên int32.
Việc triển khai Int64 được để lại như một bài tập cho người đọc (!)
Tôi đã thử sử dụng Array.BinarySearch thay vì mã hóa cứng cây, nhưng tốc độ đó chỉ bằng một nửa.
CHỈNH SỬA: Bảng tra cứu nhanh hơn nhiều so với tìm kiếm nhị phân, với chi phí sử dụng nhiều bộ nhớ hơn. Thực tế, tôi có thể sử dụng tìm kiếm nhị phân trong sản xuất, bảng tra cứu rất phức tạp để tăng tốc độ có thể bị lu mờ bởi các phần khác của phần mềm.
Lookup-Table: 439 ms
Binary-Search: 1069 ms
If-Chain: 1409 ms
Log10: 1145 ms
While: 1768 ms
String: 5153 ms
Phiên bản bảng tra cứu:
static byte[] _0000llll = new byte[0x10000];
static byte[] _FFFFllll = new byte[0x10001];
static sbyte[] _hhhhXXXXdigits = new sbyte[0x10000];
// Special cases where the high DWORD is not enough information to find out how
// many digits.
static ushort[] _lowordSplits = new ushort[12];
static sbyte[] _lowordSplitDigitsLT = new sbyte[12];
static sbyte[] _lowordSplitDigitsGE = new sbyte[12];
static Int32Extensions()
{
// Simple lookup tables for number of digits where value is
// 0000xxxx (0 .. 65535)
// or FFFFxxxx (-1 .. -65536)
precomputePositiveLo16();
precomputeNegativeLo16();
// Hiword is a little more complex
precomputeHiwordDigits();
}
private static void precomputeHiwordDigits()
{
int b = 0;
for(int hhhh = 0; hhhh <= 0xFFFF; hhhh++)
{
// For hiword hhhh, calculate integer value for loword of 0000 and FFFF.
int hhhh0000 = (unchecked(hhhh * 0x10000)); // wrap around on negatives
int hhhhFFFF = hhhh0000 + 0xFFFF;
// How many decimal digits for each?
int digits0000 = hhhh0000.Digits_IfChain();
int digitsFFFF = hhhhFFFF.Digits_IfChain();
// If same number of decimal digits, we know that when we see that hiword
// we don't have to look at the loword to know the right answer.
if(digits0000 == digitsFFFF)
{
_hhhhXXXXdigits[hhhh] = (sbyte)digits0000;
}
else
{
bool negative = hhhh >= 0x8000;
// Calculate 10, 100, 1000, 10000 etc
int tenToThePower = (int)Math.Pow(10, (negative ? digits0000 : digitsFFFF) - 1);
// Calculate the loword of the 10^n value.
ushort lowordSplit = unchecked((ushort)tenToThePower);
if(negative)
lowordSplit = unchecked((ushort)(2 + (ushort)~lowordSplit));
// Store the split point and digits into these arrays
_lowordSplits[b] = lowordSplit;
_lowordSplitDigitsLT[b] = (sbyte)digits0000;
_lowordSplitDigitsGE[b] = (sbyte)digitsFFFF;
// Store the minus of the array index into the digits lookup. We look for
// minus values and use these to trigger using the split points logic.
_hhhhXXXXdigits[hhhh] = (sbyte)(-b);
b++;
}
}
}
private static void precomputePositiveLo16()
{
for(int i = 0; i <= 9; i++)
_0000llll[i] = 1;
for(int i = 10; i <= 99; i++)
_0000llll[i] = 2;
for(int i = 100; i <= 999; i++)
_0000llll[i] = 3;
for(int i = 1000; i <= 9999; i++)
_0000llll[i] = 4;
for(int i = 10000; i <= 65535; i++)
_0000llll[i] = 5;
}
private static void precomputeNegativeLo16()
{
for(int i = 0; i <= 9; i++)
_FFFFllll[65536 - i] = 1;
for(int i = 10; i <= 99; i++)
_FFFFllll[65536 - i] = 2;
for(int i = 100; i <= 999; i++)
_FFFFllll[65536 - i] = 3;
for(int i = 1000; i <= 9999; i++)
_FFFFllll[65536 - i] = 4;
for(int i = 10000; i <= 65535; i++)
_FFFFllll[65536 - i] = 5;
}
public static int Digits_LookupTable(this int n)
{
// Split input into low word and high word.
ushort l = unchecked((ushort)n);
ushort h = unchecked((ushort)(n >> 16));
// If the hiword is 0000 or FFFF we have precomputed tables for these.
if(h == 0x0000)
{
return _0000llll[l];
}
else if(h == 0xFFFF)
{
return _FFFFllll[l];
}
// In most cases the hiword will tell us the number of decimal digits.
sbyte digits = _hhhhXXXXdigits[h];
// We put a positive number in this lookup table when
// hhhh0000 .. hhhhFFFF all have the same number of decimal digits.
if(digits > 0)
return digits;
// Where the answer is different for hhhh0000 to hhhhFFFF, we need to
// look up in a separate array to tell us at what loword the change occurs.
var splitIndex = (sbyte)(-digits);
ushort lowordSplit = _lowordSplits[splitIndex];
// Pick the correct answer from the relevant array, depending whether
// our loword is lower than the split point or greater/equal. Note that for
// negative numbers, the loword is LOWER for MORE decimal digits.
if(l < lowordSplit)
return _lowordSplitDigitsLT[splitIndex];
else
return _lowordSplitDigitsGE[splitIndex];
}
Phiên bản tìm kiếm nhị phân
public static int Digits_BinarySearch(this int n)
{
if(n >= 0)
{
if(n <= 9999) // 0 .. 9999
{
if(n <= 99) // 0 .. 99
{
return (n <= 9) ? 1 : 2;
}
else // 100 .. 9999
{
return (n <= 999) ? 3 : 4;
}
}
else // 10000 .. int.MaxValue
{
if(n <= 9_999_999) // 10000 .. 9,999,999
{
if(n <= 99_999)
return 5;
else if(n <= 999_999)
return 6;
else
return 7;
}
else // 10,000,000 .. int.MaxValue
{
if(n <= 99_999_999)
return 8;
else if(n <= 999_999_999)
return 9;
else
return 10;
}
}
}
else
{
if(n >= -9999) // -9999 .. -1
{
if(n >= -99) // -99 .. -1
{
return (n >= -9) ? 1 : 2;
}
else // -9999 .. -100
{
return (n >= -999) ? 3 : 4;
}
}
else // int.MinValue .. -10000
{
if(n >= -9_999_999) // -9,999,999 .. -10000
{
if(n >= -99_999)
return 5;
else if(n >= -999_999)
return 6;
else
return 7;
}
else // int.MinValue .. -10,000,000
{
if(n >= -99_999_999)
return 8;
else if(n >= -999_999_999)
return 9;
else
return 10;
}
}
}
}
Stopwatch sw0 = new Stopwatch();
sw0.Start();
for(int i = 0; i < size; ++i) samples[i].Digits_BinarySearch();
sw0.Stop();
Console.WriteLine($"Binary-Search: {sw0.ElapsedMilliseconds} ms");
Int64
triển khai cho LookUpTable không? Hay bạn nghĩ rằng nó quá phức tạp để thực hiện nó? Tôi muốn chạy các bài kiểm tra hiệu suất sau trên toàn bộ.
int i = 855865264;
int NumLen = i.ToString().Length;
string.TrimStart('-')
tốt hơn
Tạo một phương thức trả về tất cả các chữ số và một phương thức khác đếm chúng:
public static int GetNumberOfDigits(this long value)
{
return value.GetDigits().Count();
}
public static IEnumerable<int> GetDigits(this long value)
{
do
{
yield return (int)(value % 10);
value /= 10;
} while (value != 0);
}
Đây là cách tiếp cận trực quan hơn đối với tôi khi giải quyết vấn đề này. Tôi đã thử Log10
phương pháp này trước tiên do tính đơn giản rõ ràng của nó, nhưng nó có một số lượng lớn các trường hợp góc và các vấn đề về độ chính xác.
Tôi cũng thấy if
-chain được đề xuất trong câu trả lời khác hơi xấu xí khi nhìn vào.
Tôi biết đây không phải là phương pháp hiệu quả nhất, nhưng nó cung cấp cho bạn phần mở rộng khác để trả về các chữ số cũng như cho các mục đích sử dụng khác (bạn chỉ có thể đánh dấu nó private
nếu bạn không cần sử dụng nó bên ngoài lớp học).
Hãy nhớ rằng nó không coi dấu âm là một chữ số.
chuyển đổi thành chuỗi và sau đó bạn có thể đếm không có chữ số bằng phương thức .length. Giống:
String numberString = "855865264".toString();
int NumLen = numberString .Length;
Nó phụ thuộc chính xác những gì bạn muốn làm với các chữ số. Bạn có thể lặp lại các chữ số bắt đầu từ chữ số cuối cùng đến chữ số đầu tiên như sau:
int tmp = number;
int lastDigit = 0;
do
{
lastDigit = tmp / 10;
doSomethingWithDigit(lastDigit);
tmp %= 10;
} while (tmp != 0);
%
để lấy chữ số, và sau đó /=
cắt nó xuống.
Nếu nó chỉ để xác thực, bạn có thể làm: 887979789 > 99999999
Giả sử câu hỏi của bạn đề cập đến một số nguyên, thì những điều sau đây cũng hoạt động cho âm / dương và 0:
Math.Floor((decimal) Math.Abs(n)).ToString().Length