संस्कृति की परवाह किए बिना दशमलव मान में दशमलव स्थानों की संख्या ज्ञात करें


91

अगर एक दशमलव मान में दशमलव स्थानों की संख्या को बाहर निकालने के लिए एक संक्षिप्त और सटीक तरीका है, तो मैं सोच रहा हूं कि क्या विभिन्न संस्कृति जानकारी का उपयोग करना सुरक्षित होगा?

उदाहरण के लिए:
19.0 को 1 वापस करना चाहिए,
27.5999 को 4 वापस आना चाहिए,
19.12 को 2 वापस आना चाहिए,
आदि।

मैंने एक क्वेरी लिखी थी जिसने दशमलव स्थानों को खोजने के लिए एक स्ट्रिंग को विभाजित किया था:

int priceDecimalPlaces = price.ToString().Split('.').Count() > 1 
                  ? price.ToString().Split('.').ToList().ElementAt(1).Length 
                  : 0;

लेकिन मेरे साथ ऐसा होता है कि यह केवल उन क्षेत्रों में काम करेगा जो 'का उपयोग करते हैं।' एक दशमलव विभाजक के रूप में और इसलिए विभिन्न प्रणालियों में बहुत भंगुर है।


प्रश्न शीर्षक के अनुसार एक दशमलव
जेसी कार्टर

स्प्लिट से पहले कुछ पैटर्न के मिलान के बारे में कैसे? मूल रूप से \ d + (\ D) \ d + जहां \ _ विभाजक ((, आदि) को लौटाता है
अंशुल

7
यह एक बंद-समाप्त प्रश्न नहीं है क्योंकि यह पहले ब्लश दिखाई दे सकता है। 19.0वापसी के लिए पूछना मूल्य के आंतरिक भंडारण के बारे में 1एक कार्यान्वयन विवरण है19.0 । तथ्य यह है कि यह पूरी तरह वैध है के लिए कार्यक्रम के रूप में यह स्टोर करने के लिए है 190×10⁻¹या 1900×10⁻²या 19000×10⁻³। वे सभी समान हैं। तथ्य यह है कि यह पहला प्रतिनिधित्व का उपयोग करता है जब एक मूल्य दिया जाता है 19.0Mऔर यह तब उजागर होता है जब ToStringबिना प्रारूप विनिर्देशक का उपयोग करना केवल एक संयोग है, और एक खुश-ईश बात है। सिवाय इसके कि जब लोग ऐसे मामलों में प्रतिपादक पर भरोसा करते हैं, तो उन्हें खुशी नहीं होती।
EricE

आप एक प्रकार है कि ले जा सकता है "दशमलव स्थानों की संख्या का इस्तेमाल किया" जब यह बनाई गई है, ताकि आप मज़बूती से अलग कर सकते हैं चाहते हैं 19Mसे 19.0Mसे 19.00M, आप एक नया वर्ग है कि एक संपत्ति के रूप में अंतर्निहित कीमत और की संख्या बंडल बनाने की आवश्यकता होगी दशमलव को अन्य गुण के रूप में रखता है।
एरिक

1
भले ही दशमलव वर्ग 19 मीटर से 19.0 मीटर, 19 मीटर से "अंतर" कर सकता है? महत्वपूर्ण अंक इसके प्रमुख उपयोग मामलों में से एक की तरह हैं। 19.0m * 1.0m क्या है? लगता है कि 19.00m कह रहा है, शायद C # देव गणित गलत कर रहे हैं: P? फिर से महत्वपूर्ण अंक एक वास्तविक चीज है। यदि आपको महत्वपूर्ण अंक पसंद नहीं हैं, तो आपको संभवतः दशमलव वर्ग का उपयोग नहीं करना चाहिए।
निकॉली

जवाबों:


169

मैंने इस मुद्दे को हल करने के लिए जो के तरीके का उपयोग किया :)

decimal argument = 123.456m;
int count = BitConverter.GetBytes(decimal.GetBits(argument)[3])[2];

6
इस पर एक और नज़र डालने और इसे कार्रवाई में देखने के बाद मैं इसे चिह्नित कर रहा हूं क्योंकि इसका कारण यह है कि मेरी राय में दशमलव स्थानों को वापस करने का सबसे संक्षिप्त और सुरुचिपूर्ण तरीका है जो मैंने यहां देखा है। क्या मैं फिर से +1 करूंगा: D
जेसी कार्टर

9
decimalकोमा के बाद गणना अंक रखता है, इसीलिए आपको यह "समस्या" लगती है, आपको दशमलव को डबल और फ़िर दशमलव में फिक्स के लिए डालना होगा: BitConverter.GetBytes (दशमलव.गेट बिट्स (दशमलव) (डबल) तर्क) [3]) [] 2];
जलती हुई 12

3
यह मेरे लिए काम नहीं किया। SQL से वापस आने वाला मान 21.17 है, यह 4 अंक कह रहा है। डेटा-प्रकार को DECIMAL (12,4) के रूप में परिभाषित किया गया है, इसलिए शायद यह (एंटिटी फ्रेमवर्क का उपयोग करके) है।
पीटरएक्स

11
@ निकोली - नहीं, यह असाधारण रूप से बुरा है क्योंकि विधि दशमलव के अंतर्निहित बिट्स के स्थान पर निर्भर है - कुछ जिसके पास एक ही संख्या का प्रतिनिधित्व करने के कई तरीके हैं । आप निजी क्षेत्र के राज्य के आधार पर एक वर्ग का परीक्षण नहीं करेंगे?
m.edmondson

14
सुनिश्चित नहीं हैं कि इस बारे में सुरुचिपूर्ण या अच्छा माना जाता है। यह के रूप में के रूप में इसे प्राप्त होता है के बारे में है। कौन जानता है कि यह सभी मामलों में भी काम करता है या नहीं। सुनिश्चित करने के लिए असंभव।
usr

24

चूंकि आपूर्ति किए गए उत्तरों में से कोई भी जादू की संख्या "-0.01f" के लिए पर्याप्त नहीं था, जिसे दशमलव में परिवर्तित किया गया है .. यानी: GetDecimal((decimal)-0.01f);
मैं केवल यह मान सकता हूं कि 3 साल पहले हर किसी पर एक भारी दिमाग का वायरस हमला किया गया था :)
यहाँ वही है जो एक काम लगता है इस बुराई और राक्षसी समस्या को लागू करना, दशमलव स्थानों को गिनने की बहुत ही जटिल समस्या - बिंदु के बिना कोई स्ट्रिंग, कोई संस्कृतियाँ, बिट्स को गिनने की आवश्यकता नहीं है और न ही गणित के मंचों को पढ़ने की आवश्यकता है .. बस सरल 3 ग्रेड गणित।

public static class MathDecimals
{
    public static int GetDecimalPlaces(decimal n)
    {
        n = Math.Abs(n); //make sure it is positive.
        n -= (int)n;     //remove the integer part of the number.
        var decimalPlaces = 0;
        while (n > 0)
        {
            decimalPlaces++;
            n *= 10;
            n -= (int)n;
        }
        return decimalPlaces;
    }
}

private static void Main(string[] args)
{
    Console.WriteLine(1/3m); //this is 0.3333333333333333333333333333
    Console.WriteLine(1/3f); //this is 0.3333333

    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.0m));                  //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(1/3m));                  //28
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)(1 / 3f)));     //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-1.123m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces(43.12345m));             //5
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0));                     //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.01m));                 //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-0.001m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.00000001f)); //8
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.0001234f));   //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.01f));        //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.01f));       //2
}

6
आपका समाधान कई मामलों में विफल हो जाएगा, जिसमें अनुगामी शून्य शामिल हैं और अंक SIGNIFICANT हैं। 0.01m * 2.0m = क्रूज़। 3 अंक होना चाहिए, आपकी विधि वापस आती है 2. आप गलत तरीके से समझ रहे हैं कि जब आप 0.01f को दशमलव में डालते हैं तो क्या होता है। फ़्लोटिंग पॉइंट स्वाभाविक रूप से सटीक नहीं हैं, इसलिए 0.01f के लिए संग्रहीत वास्तविक बाइनरी मान सटीक नहीं है। जब आप दशमलव (एक बहुत ही संरचित संख्या अंकन) के लिए जाते हैं, तो आपको 0.01m (आप वास्तव में होबम प्राप्त नहीं कर सकते हैं)। GetBits समाधान वास्तव में दशमलव से अंकों की संख्या प्राप्त करने के लिए सही है। आप दशमलव में कैसे परिवर्तित होते हैं यह प्रमुख है।
निकोली

2
@ निचौली फ़्लोरम 0.02m के बराबर है .. अनुगामी शून्य महत्वपूर्ण नहीं हैं। ओपी शीर्षक में "संस्कृति की परवाह किए बिना" पूछ रहा है और इससे भी अधिक विशिष्ट व्याख्या करता है ".. यह अलग-अलग संस्कृति जानकारी का उपयोग करने के लिए सुरक्षित होगा .." - इसलिए मुझे लगता है कि मेरा जवाब दूसरों की तुलना में और भी अधिक मान्य है।
GY

6
ओपी ने विशेष रूप से कहा: "19.0 को 1 वापस आना चाहिए"। यह कोड उस मामले में विफल रहता है।
daniloquio

9
शायद यह वही नहीं है जो ओपी चाहता था, लेकिन यह उत्तर इस सवाल के शीर्ष उत्तर की तुलना में मेरी आवश्यकताओं के लिए बेहतर है
आर्सेन ज़ाहरे

2
पहली दो पंक्तियों के साथ प्रतिस्थापित किया जाना चाहिए n = n % 1; if (n < 0) n = -n;क्योंकि मूल्य से बड़ा int.MaxValueएक OverflowException, कारण होगा 2147483648.12345
घृणा

24

मैं शायद @ निर्धारण के उत्तर में समाधान का उपयोग करूंगा

हालाँकि, जबकि दशमलव संरचना में दशमलव की संख्या प्राप्त करने की कोई विधि नहीं है, आप द्विआधारी प्रतिनिधित्व को निकालने के लिए Decimal.GetBits को कॉल कर सकते हैं , फिर पूर्णांक की संख्या की गणना करने के लिए पूर्णांक मान और पैमाने का उपयोग करें।

यह संभवतः एक स्ट्रिंग के रूप में प्रारूपण करने से अधिक तेज़ होगा, हालांकि आपको अंतर को नोटिस करने के लिए एक बहुत अधिक दशमलव का प्रसंस्करण करना होगा।

मैं एक अभ्यास के रूप में कार्यान्वयन को छोड़ दूंगा।


1
धन्यवाद @Joe कि यह संपर्क करने का एक बहुत साफ तरीका है। इस बात पर निर्भर करता है कि मेरा बॉस दूसरे उपाय का उपयोग करने के बारे में कैसा महसूस करता है, मैं आपके विचार को लागू करने पर एक नज़र डालूंगा। निश्चित रूप से एक मजेदार व्यायाम होगा :)
जेसी कार्टर

17

Burn_LEGION की पोस्ट में दशमलव बिंदु दिखाए जाने के बाद अंकों की संख्या खोजने के लिए सबसे अच्छे समाधानों में से एक है ।

यहाँ मैं STSdb ​​फोरम लेख के कुछ हिस्सों का उपयोग कर रहा हूँ: दशमलव बिंदु के बाद अंकों की संख्या

MSDN में हम निम्नलिखित स्पष्टीकरण पढ़ सकते हैं:

"एक दशमलव संख्या एक फ़्लोटिंग-पॉइंट मान है जिसमें एक चिन्ह, एक संख्यात्मक मान होता है, जहाँ मूल्य का प्रत्येक अंक 0 से 9 तक होता है, और एक स्केलिंग कारक जो एक अस्थायी दशमलव बिंदु की स्थिति को इंगित करता है जो अभिन्न और आंशिक को अलग करता है संख्यात्मक मान के कुछ हिस्से। "

और भी:

"दशमलव मान के द्विआधारी प्रतिनिधित्व में 1-बिट चिन्ह, 96-बिट पूर्णांक संख्या और 96-बिट पूर्णांक को विभाजित करने के लिए उपयोग किया जाने वाला स्केलिंग कारक होता है और निर्दिष्ट करता है कि इसका कौन सा भाग दशमलव अंश है। स्केलिंग कारक। अनुमानित रूप से संख्या 10, जो 0 से 28 तक के एक घातांक तक बढ़ा दी गई है। "

आंतरिक स्तर पर दशमलव मान चार पूर्णांक मानों द्वारा दर्शाया जाता है।

दशमलव आंतरिक प्रतिनिधित्व

आंतरिक प्रतिनिधित्व प्राप्त करने के लिए सार्वजनिक रूप से उपलब्ध GetBits फ़ंक्शन है। फ़ंक्शन एक int [] सरणी देता है:

[__DynamicallyInvokable] 
public static int[] GetBits(decimal d)
{
    return new int[] { d.lo, d.mid, d.hi, d.flags };
}

लौटे सरणी के चौथे तत्व में एक स्केल फैक्टर और एक चिन्ह होता है। और जैसा कि MSDN कहता है कि स्केलिंग फैक्टर संख्या 10 है, जो 0 से 28 तक के एक घातांक तक बढ़ा है। ठीक यही हमें चाहिए।

इस प्रकार, उपरोक्त सभी जांचों के आधार पर हम अपनी विधि का निर्माण कर सकते हैं:

private const int SIGN_MASK = ~Int32.MinValue;

public static int GetDigits4(decimal value)
{
    return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}

यहां साइन को नजरअंदाज करने के लिए एक SIGN_MASK का उपयोग किया जाता है। तार्किक होने के बाद और हमने वास्तविक स्केल फैक्टर प्राप्त करने के लिए 16 बिट्स के साथ परिणाम को दाईं ओर स्थानांतरित कर दिया है। यह मान, अंततः दशमलव बिंदु के बाद अंकों की संख्या को इंगित करता है।

ध्यान दें कि यहाँ MSDN भी कहता है कि स्केलिंग फैक्टर किसी भी अनुगामी शून्य को दशमलव संख्या में संरक्षित करता है। ट्रेलिंग शून्य अंकगणितीय या तुलनात्मक संचालन में एक दशमलव संख्या के मूल्य को प्रभावित नहीं करते हैं। हालाँकि, ट्रेसिंग विधि द्वारा ट्रेलिंग जीरो को प्रकट किया जा सकता है यदि एक उपयुक्त प्रारूप स्ट्रिंग लागू किया जाता है।

यह समाधान सबसे अच्छा लगता है, लेकिन रुको, और भी बहुत कुछ है। C # में निजी विधियों का उपयोग करके हम झंडे के क्षेत्र तक सीधी पहुंच बनाने और अंतर सरणी के निर्माण से बचने के लिए अभिव्यक्ति का उपयोग कर सकते हैं:

public delegate int GetDigitsDelegate(ref Decimal value);

public class DecimalHelper
{
    public static readonly DecimalHelper Instance = new DecimalHelper();

    public readonly GetDigitsDelegate GetDigits;
    public readonly Expression<GetDigitsDelegate> GetDigitsLambda;

    public DecimalHelper()
    {
        GetDigitsLambda = CreateGetDigitsMethod();
        GetDigits = GetDigitsLambda.Compile();
    }

    private Expression<GetDigitsDelegate> CreateGetDigitsMethod()
    {
        var value = Expression.Parameter(typeof(Decimal).MakeByRefType(), "value");

        var digits = Expression.RightShift(
            Expression.And(Expression.Field(value, "flags"), Expression.Constant(~Int32.MinValue, typeof(int))), 
            Expression.Constant(16, typeof(int)));

        //return (value.flags & ~Int32.MinValue) >> 16

        return Expression.Lambda<GetDigitsDelegate>(digits, value);
    }
}

यह संकलित कोड GetDigits फ़ील्ड को सौंपा गया है। ध्यान दें कि फ़ंक्शन दशमलव मान को रेफरी के रूप में प्राप्त करता है, इसलिए कोई वास्तविक नकल नहीं की जाती है - केवल मूल्य के लिए एक संदर्भ। DecimalHelper से GetDigits फ़ंक्शन का उपयोग करना आसान है:

decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);

दशमलव मानों के लिए दशमलव बिंदु के बाद अंकों की संख्या प्राप्त करने के लिए यह सबसे तेज़ संभव विधि है।


3
दशमलव r = (दशमलव) -0.01f; और समाधान विफल रहता है। (सभी उत्तरों पर मैंने इस पृष्ठ में देखा ...) :)
GY

5
नोट: संपूर्ण (दशमलव) 0.01f चीज़ के बारे में, आप एक अस्थायी बिंदु को कास्टिंग कर रहे हैं, स्वाभाविक रूप से नहीं, कुछ दशमलव की तरह बहुत संरचित। Console.WriteLine ((दशमलव) 0.01f) के आउटपुट पर एक नज़र डालें। कास्ट ACTUALLY में गठित होने वाले दशमलव में 3 अंक होते हैं, यही कारण है कि प्रदान किए गए सभी समाधान 2 के बजाय 3 कहते हैं। सब कुछ वास्तव में अपेक्षित रूप से काम कर रहा है, "समस्या" है कि आप अस्थायी बिंदु मानों के सटीक होने की उम्मीद कर रहे हैं। वो नहीं हैं।
निकोली

@Nicholi आपका बिंदु में विफल रहता है जब आप उस एहसास 0.01और 0.010वास्तव में बराबर हैं संख्या । इसके अलावा, यह विचार कि एक संख्यात्मक डेटा प्रकार में कुछ प्रकार के "अंकों का उपयोग किया गया" शब्दार्थ है, जिस पर भरोसा किया जा सकता है, पूरी तरह से गलत है ("अंकों की अनुमति दी गई संख्या के साथ भ्रमित नहीं होना") प्रस्तुति को भ्रमित न करें (प्रदर्शन का प्रदर्शन)। एक विशेष आधार में एक संख्या का मूल्य, उदाहरण के लिए, अंतर्निहित मूल्य के साथ द्विआधारी विस्तार 111 द्वारा इंगित मूल्य का दशमलव विस्तार! दोहराए जाने के लिए, संख्याएं अंक नहीं हैं, और न ही वे अंकों से बने हैं
एरिक

6
वे मूल्य में बराबर हैं, लेकिन महत्वपूर्ण अंकों में नहीं। जो कि दशमलव वर्ग का एक बड़ा उपयोग मामला है। अगर मैंने पूछा कि शाब्दिक होपम में कितने अंक हैं, तो क्या आप केवल 2 कहेंगे? भले ही दुनिया भर के गणित / विज्ञान के शिक्षक आपको बताएंगे कि अंतिम 0 महत्वपूर्ण है? हम जिस समस्या का जिक्र कर रहे हैं, वह फ्लोटिंग पॉइंट्स से दशमलव तक घटने से प्रकट होती है। स्वयं GetBits का उपयोग नहीं है, जो बिल्कुल वैसा ही कर रहा है जैसा कि यह प्रलेखित है। यदि आप महत्वपूर्ण अंकों की परवाह नहीं करते हैं, तो हाँ आपके पास एक समस्या है और पहली जगह में दशमलव वर्ग का उपयोग नहीं किया जाना चाहिए।
निकोली

1
@ विंडसर जब तक मुझे याद है, कोई पकड़ नहीं था - यह दोनों तरीकों से काम करना चाहिए।
क्रिस्टियान दिमित्रोव

13

दशमलव के आंतरिक प्रतिनिधित्व पर भरोसा करना अच्छा नहीं है।

इस बारे में कैसा है:

    int CountDecimalDigits(decimal n)
    {
        return n.ToString(System.Globalization.CultureInfo.InvariantCulture)
                //.TrimEnd('0') uncomment if you don't want to count trailing zeroes
                .SkipWhile(c => c != '.')
                .Skip(1)
                .Count();
    }

11

आप InvariantCulture का उपयोग कर सकते हैं

string priceSameInAllCultures = price.ToString(System.Globalization.CultureInfo.InvariantCulture);

एक और संभावना कुछ ऐसा करने की होगी:

private int GetDecimals(decimal d, int i = 0)
{
    decimal multiplied = (decimal)((double)d * Math.Pow(10, i));
    if (Math.Round(multiplied) == multiplied)
        return i;
    return GetDecimals(d, i+1);
}

यह मुझे दशमलव में दशमलव स्थानों की संख्या को खोजने में कैसे मदद करता है? मुझे दशमलव को स्ट्रिंग में परिवर्तित करने में कोई समस्या नहीं है जो किसी भी संस्कृति में अच्छी है। प्रश्न के अनुसार मैं दशमलव पर आने वाले दशमलव स्थानों की संख्या ज्ञात करने का प्रयास कर रहा हूं
जेसी कार्टर

@ जेसेकेटर: इसका मतलब है कि आप हमेशा विभाजित कर सकते हैं .
ऑस्टिन सैलूनन

@AustinSalonen सच में? मुझे पता नहीं था कि InvariantCulture का उपयोग दशमलव विभाजक के रूप में एक अवधि के उपयोग को लागू करेगा
जेसी कार्टर

जैसा कि आपने पहले किया था, यह हमेशा एक के साथ स्ट्रिंग करने के लिए मूल्य डालेगा। दशमलव विभाजक के रूप में। लेकिन यह मेरी राय में सबसे सुंदर तरीका नहीं है ...
फिक्स


8

यहाँ पर ज्यादातर लोग इस बात से अनजान हैं कि दशमलव ट्रेलिंग जीरो को भंडारण और मुद्रण के लिए महत्वपूर्ण मानता है।

तो 0.1 m, 0.10 m और 0.100 m की तुलना बराबर हो सकती है, वे अलग-अलग तरीके से संग्रहित किए जाते हैं (क्रमशः मूल्य / स्केल 1/1, 10/2 और 100/3,), और क्रमशः 0.1, 0.10 और 0.100 के रूप में मुद्रित किया जाएगा। , द्वारा ToString()

जैसे, "बहुत अधिक सटीक" रिपोर्ट करने वाले समाधान वास्तव में सही सटीकता की रिपोर्ट कर रहे हैं decimal

इसके अलावा, गणित-आधारित समाधान (जैसे कि 10 की शक्तियों से गुणा करना) की संभावना बहुत धीमी होगी (अंकगणित के लिए डबल की तुलना में दशमलव ~ 40x धीमा है, और आप फ़्लोटिंग-पॉइंट में मिश्रण नहीं करना चाहते हैं क्योंकि यह अप्रासंगिकता को लागू करने की संभावना है )। इसी तरह, ट्रंकटिंग के साधन के रूप में intया उसके लिए कास्टिंग longत्रुटि-प्रवण है ( decimalउन दोनों की तुलना में बहुत अधिक रेंज है - यह लगभग 96-बिट पूर्णांक पर आधारित है)।

हालांकि, जैसा कि सुरुचिपूर्ण नहीं है, निम्नलिखित संभावना को प्राप्त करने के लिए सबसे तेज़ तरीके में से एक होगा (जब "शून्य स्थानों को छोड़कर दशमलव स्थान" के रूप में परिभाषित किया गया हो):

public static int PrecisionOf(decimal d) {
  var text = d.ToString(System.Globalization.CultureInfo.InvariantCulture).TrimEnd('0');
  var decpoint = text.IndexOf('.');
  if (decpoint < 0)
    return 0;
  return text.Length - decpoint - 1;
}

अपरिवर्तनीय संस्कृति एक 'की गारंटी देती है।' दशमलव बिंदु के रूप में, अनुगामी शून्य को छंटनी की जाती है, और फिर यह देखने की बात है कि दशमलव बिंदु के बाद कितने पद बने रहते हैं (यदि एक भी है)।

संपादित करें: बदले हुए प्रकार को इंट में बदलें


1
@mvmorten सुनिश्चित नहीं हैं कि आपको ऐसा क्यों लगा कि रिटर्न प्रकार को इंट में बदलना आवश्यक था; बाइट अधिक सटीक रूप से लौटाए गए मूल्य का प्रतिनिधित्व करता है: अहस्ताक्षरित और छोटी सीमा (0-29, व्यवहार में)।
ज़स्तई

1
मैं मानता हूं कि पुनरावृत्ति और गणना आधारित समाधान धीमे हैं (इसके अलावा शून्य के पीछे का हिसाब नहीं)। हालांकि, इसके लिए एक स्ट्रिंग आवंटित करना और इसके बजाय उस पर काम करना भी विशेष रूप से महत्वपूर्ण संदर्भों में और धीमी जीसी के साथ प्रदर्शन करने के लिए सबसे अधिक प्रदर्शन वाली बात नहीं है। पॉइंटर लॉजिक के जरिए पैमाना हासिल करना तेजी से और आवंटन मुक्त होना एक अच्छा सौदा है।
मार्टिन टिलो शमित्ज़

हां, पैमाना प्राप्त करना अधिक कुशलता से किया जा सकता है - लेकिन इसमें अनुगामी शून्य शामिल होंगे। और उन्हें हटाने के लिए पूर्णांक भाग पर अंकगणित करने की आवश्यकता होती है।
जस्तई

6

और यहाँ एक और तरीका है, SqlDecimal प्रकार का उपयोग करें जिसमें दशमलव के दाईं ओर अंकों की गणना के साथ एक पैमाना गुण है। अपने दशमलव मान को SqlDecimal में जमा करें और फिर स्केल पर पहुँचें।

((SqlDecimal)(decimal)yourValue).Scale

1
Microsoft संदर्भ कोड को देखते हुए , SqlDecimal को आंतरिक रूप से कास्टिंग करना GetBytesइसलिए इसे एक असुरक्षित संदर्भ में बाइट्स तक पहुंचने के बजाय बाइट सरणी आवंटित करता है। यहां तक ​​कि एक नोट भी है और संदर्भ कोड में कोड बताते हुए कहा कि इसके बजाय वे ऐसा कैसे कर सकते हैं। वे क्यों नहीं थे मेरे लिए एक रहस्य है। मैं इसके बारे में स्पष्ट रहूंगा और इस कास्ट में जीसी एलोक को छिपाने के बजाय सीधे स्केल बिट्स तक पहुंच सकता हूं, क्योंकि यह बहुत स्पष्ट नहीं है कि यह हुड के नीचे क्या करता है।
मार्टिन टिलो शमित्ज़

4

अब तक, लगभग सभी सूचीबद्ध समाधान जीसी मेमोरी आवंटित कर रहे हैं, जो कि चीजों को करने के लिए सी # तरीका है, लेकिन प्रदर्शन से दूर महत्वपूर्ण वातावरण में आदर्श है। (जो लोग लूप का आवंटन नहीं करते हैं और ज़ीरो को ध्यान में नहीं रखते हैं।)

तो जीसी एलोक्स से बचने के लिए, आप स्केल बिट्स को एक असुरक्षित संदर्भ में एक्सेस कर सकते हैं। यह नाजुक लग सकता है, लेकिन Microsoft के संदर्भ स्रोत के अनुसार , दशमलव का संरचनात्मक लेआउट अनुक्रमिक है और यहां तक ​​कि इसमें एक टिप्पणी है, फ़ील्ड के क्रम को बदलने के लिए नहीं:

    // NOTE: Do not change the order in which these fields are declared. The
    // native methods in this class rely on this particular order.
    private int flags;
    private int hi;
    private int lo;
    private int mid;

जैसा कि आप देख सकते हैं, यहां पहला इंट झंडे का मैदान है। प्रलेखन से और जैसा कि अन्य टिप्पणियों में यहां बताया गया है, हम जानते हैं कि केवल 16-24 से बिट्स पैमाने को एन्कोड करते हैं और हमें 31 वें बिट से बचने की जरूरत है जो साइन को एनकोड करता है। चूंकि int 4 बाइट्स का आकार है, इसलिए हम इसे सुरक्षित रूप से कर सकते हैं:

internal static class DecimalExtensions
{
  public static byte GetScale(this decimal value)
  {
    unsafe
    {
      byte* v = (byte*)&value;
      return v[2];
    }
  }
}

बाइट्स सरणी या ToString रूपांतरणों का कोई GC आबंटन नहीं होने के कारण यह सबसे अच्छा प्रदर्शन समाधान होना चाहिए। मैंने इसे .Net 4.x और .Net 3.5 के खिलाफ यूनिटी 2019.1 में परीक्षण किया है। अगर कोई ऐसा संस्करण है जहाँ यह विफल होता है, तो कृपया मुझे बताएँ।

संपादित करें:

असुरक्षित कोड के बाहर व्यावहारिक रूप से समान पॉइंटर लॉजिक को प्राप्त करने के लिए एक स्पष्ट संरचना लेआउट का उपयोग करने की संभावना के बारे में याद दिलाने के लिए @Zastai के लिए धन्यवाद:

[StructLayout(LayoutKind.Explicit)]
public struct DecimalHelper
{
    const byte k_SignBit = 1 << 7;

    [FieldOffset(0)]
    public decimal Value;

    [FieldOffset(0)]
    public readonly uint Flags;
    [FieldOffset(0)]
    public readonly ushort Reserved;
    [FieldOffset(2)]
    byte m_Scale;
    public byte Scale
    {
        get
        {
            return m_Scale;
        }
        set
        {
            if(value > 28)
                throw new System.ArgumentOutOfRangeException("value", "Scale can't be bigger than 28!")
            m_Scale = value;
        }
    }
    [FieldOffset(3)]
    byte m_SignByte;
    public int Sign
    {
        get
        {
            return m_SignByte > 0 ? -1 : 1;
        }
    }
    public bool Positive
    {
        get
        {
            return (m_SignByte & k_SignBit) > 0 ;
        }
        set
        {
            m_SignByte = value ? (byte)0 : k_SignBit;
        }
    }
    [FieldOffset(4)]
    public uint Hi;
    [FieldOffset(8)]
    public uint Lo;
    [FieldOffset(12)]
    public uint Mid;

    public DecimalHelper(decimal value) : this()
    {
        Value = value;
    }

    public static implicit operator DecimalHelper(decimal value)
    {
        return new DecimalHelper(value);
    }

    public static implicit operator decimal(DecimalHelper value)
    {
        return value.Value;
    }
}

मूल समस्या को हल करने के लिए, आप के अलावा सभी क्षेत्रों दूर पट्टी सकता है Valueऔर Scaleलेकिन शायद यह किसी को उन सब के लिए उपयोगी हो सकता है।


1
आप अन्वेषण लेआउट के साथ अपनी स्वयं की संरचना को कोड करके असुरक्षित कोड से भी बच सकते हैं - स्थिति 0 पर एक दशमलव डालें, फिर उपयुक्त स्थानों पर बाइट्स / इनट्स। कुछ इस तरह:[StructLayout(LayoutKind.Explicit)] public struct DecimalHelper { [FieldOffset(0)] public decimal Value; [FieldOffset(0)] public uint Flags; [FieldOffset(0)] public ushort Reserved; [FieldOffset(2)] public byte Scale; [FieldOffset(3)] public DecimalSign Sign; [FieldOffset(4)] public uint ValuePart1; [FieldOffset(8)] public ulong ValuePart2; }
Zastai

धन्यवाद @Zastai, अच्छी बात है। मैंने उस दृष्टिकोण को भी शामिल कर लिया है। :)
मार्टिन टिलो शमित्ज़

1
एक बात ध्यान दें: 0-28 रेंज के बाहर स्केल सेट करने से टूट-फूट होती है। ToString () काम करता है, लेकिन अंकगणित विफल हो जाता है।
ज़स्तई

फिर से धन्यवाद @Zastai, मैंने उसके लिए एक चेक जोड़ा है :)
मार्टिन

एक और बात: यहां कई लोग ट्रेलिंग जीरो को ध्यान में नहीं रखना चाहते थे। यदि आप const decimal Foo = 1.0000000000000000000000000000m;एक दशमलव को तब से विभाजित करते हैं, तो इसे सबसे कम संभव पैमाने पर पुनर्विक्रय करेंगे (यानी अब दशमलव दशमलव को शामिल नहीं किया जाएगा)। मैंने यह देखने के लिए यह निर्धारित नहीं किया है कि यह स्ट्रिंग-आधारित दृष्टिकोण से तेज है या नहीं, हालांकि मैंने कहीं और सुझाव दिया था।
ज़स्तई

2

मैंने कल एक संक्षिप्त विधि लिखी है जो दशमलव स्थानों की संख्या को किसी भी स्ट्रिंग विभाजन या संस्कृतियों पर भरोसा किए बिना लौटाता है जो आदर्श है:

public int GetDecimalPlaces(decimal decimalNumber) { // 
try {
    // PRESERVE:BEGIN
        int decimalPlaces = 1;
        decimal powers = 10.0m;
        if (decimalNumber > 0.0m) {
            while ((decimalNumber * powers) % 1 != 0.0m) {
                powers *= 10.0m;
                ++decimalPlaces;
            }
        }
return decimalPlaces;

@ फिक्स-जैसे-कोडिंग आपके दूसरे उत्तर के समान है, हालांकि कुछ इस तरह के लिए मैं पुनरावृत्ति का उपयोग करने के बजाय पुनरावृत्ति दृष्टिकोण का पक्ष लेता हूं
जेसी कार्टर

मूल पोस्ट कहा गया है कि: 19.0 should return 1। यह समाधान हमेशा 1 दशमलव स्थान की न्यूनतम मात्रा मान लेगा और ट्रेलिंग शून्य को अनदेखा करेगा। दशमलव में वे हो सकते हैं जैसे यह एक स्केल फैक्टर का उपयोग करता है। स्केल फैक्टर को तत्व के बाइट 16-24 के रूप में एक्सेस किया जा सकता है, एरे में 3 के साथ एलीमेंट Decimal.GetBytes()लॉजिक का उपयोग करके या इंडेक्स लॉजिक के साथ।
मार्टिन टिलो शमित्ज़

2

मैं क्लेमेंट के उत्तर के समान कुछ का उपयोग कर रहा हूं:

private int GetSignificantDecimalPlaces(decimal number, bool trimTrailingZeros = true)
{
  string stemp = Convert.ToString(number);

  if (trimTrailingZeros)
    stemp = stemp.TrimEnd('0');

  return stemp.Length - 1 - stemp.IndexOf(
         Application.CurrentCulture.NumberFormat.NumberDecimalSeparator);
}

Application.CurrentCulture तक पहुँच प्राप्त करने के लिए System.Windows.Forms का उपयोग करना याद रखें


1

तुम कोशिश कर सकते हो:

int priceDecimalPlaces =
        price.ToString(System.Globalization.CultureInfo.InvariantCulture)
              .Split('.')[1].Length;

7
क्या यह विफल नहीं होगा जब दशमलव एक पूरी संख्या है? [1]
सिल्वरमाइंड

1

मैं अपने कोड में निम्नलिखित तंत्र का उपयोग करता हूं

  public static int GetDecimalLength(string tempValue)
    {
        int decimalLength = 0;
        if (tempValue.Contains('.') || tempValue.Contains(','))
        {
            char[] separator = new char[] { '.', ',' };
            string[] tempstring = tempValue.Split(separator);

            decimalLength = tempstring[1].Length;
        }
        return decimalLength;
    }

दशमलव इनपुट = 3.376; var instring = input.ToString ();

GetDecimalLength (इंस्ट्रिंग) को कॉल करें


1
यह मेरे लिए काम नहीं करता है क्योंकि टोमरिंग () का दशांश मान का प्रतिनिधित्व मेरे डेटा के अंत में "00" जोड़ता है - मैं SQL सर्वर से एक दशमलव (12,4) डेटाटाइप का उपयोग कर रहा हूं।
पीटर एक्स

क्या आप अपने डेटा को c # टाइप दशमलव में डाल सकते हैं और समाधान का प्रयास कर सकते हैं। मेरे लिए जब मैं c # दशमलव मूल्य पर Tostring () का उपयोग करता हूं तो मुझे कभी भी "00" दिखाई नहीं देता।
श्रीकांत

1

पुनरावृत्ति का उपयोग आप कर सकते हैं:

private int GetDecimals(decimal n, int decimals = 0)  
{  
    return n % 1 != 0 ? GetDecimals(n * 10, decimals + 1) : decimals;  
}

मूल पोस्ट कहा गया है कि: 19.0 should return 1। यह घोल ज़ीरो को पीछे छोड़ देगा। दशमलव में वे हो सकते हैं जैसे यह एक स्केल फैक्टर का उपयोग करता है। स्केल फैक्टर को तत्व के बाइट्स 16-24 के रूप में एक्सेस 3 में इंडेक्स 3 के साथ Decimal.GetBytes()या पॉइंटर लॉजिक का उपयोग करके एक्सेस किया जा सकता है ।
मार्टिन टिलो शमित्ज़


0

मैं इस विधि का उपयोग करने का सुझाव देता हूं:

    public static int GetNumberOfDecimalPlaces(decimal value, int maxNumber)
    {
        if (maxNumber == 0)
            return 0;

        if (maxNumber > 28)
            maxNumber = 28;

        bool isEqual = false;
        int placeCount = maxNumber;
        while (placeCount > 0)
        {
            decimal vl = Math.Round(value, placeCount - 1);
            decimal vh = Math.Round(value, placeCount);
            isEqual = (vl == vh);

            if (isEqual == false)
                break;

            placeCount--;
        }
        return Math.Min(placeCount, maxNumber); 
    }

0

एक दशमलव विस्तार विधि के रूप में जो ध्यान में रखता है:

  • विभिन्न संस्कृतियाँ
  • पूर्ण संख्या
  • ऋणात्मक संख्या
  • दशमलव स्थान पर ट्रेलिंग सेट शून्य (जैसे 1.2300M 2 नहीं 4 लौटाएगा)
public static class DecimalExtensions
{
    public static int GetNumberDecimalPlaces(this decimal source)
    {
        var parts = source.ToString(CultureInfo.InvariantCulture).Split('.');

        if (parts.Length < 2)
            return 0;

        return parts[1].TrimEnd('0').Length;
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.