文化に関係なく、小数点以下の桁数を求めます


91

さまざまなカルチャ情報で安全に使用できる、小数点以下の桁数を(intとして)引き出すための簡潔で正確な方法があるかどうか疑問に思っていますか?

例:
19.0は1を返し、
27.5999は4を返し、
19.12は2を返します

小数点以下の桁数を見つけるために、ピリオドで文字列を分割するクエリを作成しました。

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

しかし、これは「。」を使用するリージョンでのみ機能することがわかりました。小数点記号として使用されるため、さまざまなシステム間で非常に脆弱です。


質問のタイトルによる小数
Jesse Carter

分割前のパターンマッチングはどうですか?基本的に\ d +(\ D)\ d +ここで、\ Dは区切り文字(。など)を返します
Anshul 2012年

7
これは、最初は赤面する可能性があるため、制限のない質問ではありません。求め19.0復帰することが1ある実装の詳細値の内部記憶装置に関する19.0。事実、プログラムがこれを190×10⁻¹または1900×10⁻²またはとして保存することは完全に合法です19000×10⁻³。それらはすべて等しい。の値が与えられたときに最初の表現19.0Mを使用ToStringし、フォーマット指定子なしで使用したときにこれが公開されるという事実は、単なる偶然であり、幸せなことです。人々が指数に頼るべきではない場合に指数に頼るのは幸せではないことを除いて。
ErikE 2015

あなたがしたい場合は、作成されたときに運ぶことができるタイプのあなたが確実に区別できるようにすることを、「小数点以下の桁数が使用さ」19Mから19.0Mから19.00M、あなたは一つの特性や数など根本的な価値をバンドルした新しいクラスを作成する必要があります別のプロパティとして小数点以下の桁数。
ErikE 2015

1
Decimalクラスは19mを「区別」できますが、19.0mと19.00mを区別できますか?有効数字は、その主要なユースケースの1つに似ています。19.0m * 1.0mとは何ですか?19.00mと言っているようですが、おそらくC#開発者は数学を間違ってやっています:P?繰り返しますが、有効数字は本物です。有効数字が気に入らない場合は、Decimalクラスを使用しないでください。
ニコリ2015

回答:


168

私はこの問題を解決するためにジョーの方法を使用しました:)

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

6
これをさらに調べて実際に動作していることを確認した後、これを答えとしてマークします。これは、私の意見では、ここで見た小数点以下の桁数を返す最も簡潔でエレガントな方法です。できればまた+1します:D
Jesse Carter

9
decimalコマの後にカウント桁を保持するため、この「問題」が見つかります。修正するには、10進数をdoubleにキャストし、再度10進数にキャストする必要があります。BitConverter.GetBytes(decimal.GetBits((decimal)(double)argument)[3])[ 2];
burning_LEGION 2013

3
これは私にはうまくいきませんでした。SQLから返される値は21.17で、4桁です。データ型はDECIMAL(12,4)として定義されているので、おそらくそれだけです(Entity Frameworkを使用)。
PeterX 2014年

11
@ Nicholi-いいえ、これは非常に悪いことです。これは、メソッドが小数の基になるビットの配置に依存しているためです。これには、同じ数値を表す多くの方法あります。プライベートフィールドの状態に基づいてクラスをテストしませんか?
m.edmondson 2015

14
これについて何がエレガントで素敵なのかわからない。これは、それが得るのとほぼ同じくらい難解です。それがすべての場合でさえ機能するかどうか誰が知っていますか。確認することは不可能です。
usr 2015

24

提供された回答はどれも、マジックナンバー「-0.01f」を10進数に変換するのに十分ではなかったので..すなわち: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
末尾のゼロを含み、数字が重要である多くの場合、ソリューションは失敗します。0.01m * 2.0m = 0.020m。3桁である必要があり、メソッドは2を返します。0.01fをDecimalにキャストするとどうなるかを誤って理解しているようです。浮動小数点は本質的に正確ではないため、0.01fに格納されている実際のバイナリ値は正確ではありません。Decimal(非常に構造化された数値表記)にキャストすると、0.01mを取得できない場合があります(実際には0.010mを取得します)。GetBitsソリューションは、実際には10進数から桁数を取得するのに適しています。10進数に変換する方法が重要です。
ニコリ2015

2
@ Nicholi0.020mは0.02mに等しいです。後続のゼロは重要ではありません。OPはタイトルに「文化に関係なく」と質問しており、さらに具体的に「異なる文化情報間で安全に使用できる」と説明しています。したがって、私の答えは他の人よりもさらに有効だと思います。
GY 2015

6
OPは具体的に言った:「19.0は1を返す必要があります」。その場合、このコードは失敗します。
daniloquio 2016年

9
これはOPが望んでいたことではないかもしれませんが、この回答は、この質問の上位の回答よりも私のニーズに適しています
Arsen Zahray 2017年

2
n = n % 1; if (n < 0) n = -n;より大きい値int.MaxValueOverflowException、などを引き起こすため、最初の2行はに置き換える必要があります2147483648.12345
2017

23

私はおそらく@fixagonの答えの解決策を使用するでしょう。

ただし、Decimal構造体には小数の数を取得するメソッドがありませんが、Decimal.GetBitsを呼び出してバイナリ表現を抽出し、整数値とスケールを使用して小数の数を計算できます。

これはおそらく文字列としてフォーマットするよりも高速ですが、違いに気付くには非常に多くの小数を処理する必要があります。

実装は演習として残しておきます。


1
@Joeに感謝します。これは、非常に優れたアプローチ方法です。上司が他のソリューションの使用についてどのように感じているかに応じて、私はあなたのアイデアの実装を検討します。間違いなく楽しいエクササイズになります:)
Jesse Carter

17

小数点以下の桁数を見つけるための最良の解決策の1つは、burning_LEGIONの投稿に示されています。

ここでは、STSdbフォーラムの記事の一部を使用しています:小数点以下の桁数

MSDNでは、次の説明を読むことができます。

「10進数は、符号、値の各桁が0〜9の範囲の数値、および整数と小数を分離する浮動小数点の位置を示すスケーリング係数で構成される浮動小数点値です。数値の一部。」

そしてまた:

「10進値の2進表現は、1ビットの符号、96ビットの整数、および96ビットの整数を除算してその小数部を指定するために使用されるスケーリング係数で構成されます。スケーリング係数は次のとおりです。暗黙的に数値10は、0から28の範囲の指数に引き上げられます。」

内部レベルでは、10進値は4つの整数値で表されます。

10進数の内部表現

内部表現を取得するための公開されているGetBits関数があります。この関数はint []配列を返します。

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

返される配列の4番目の要素には、スケール係数と符号が含まれています。そして、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は、スケーリング係数も10進数の後続ゼロを保持すると述べていることに注意してください。末尾のゼロは、算術演算または比較演算の10進数の値には影響しません。ただし、適切なフォーマット文字列が適用されている場合、後続のゼロはToStringメソッドによって明らかになる可能性があります。

このソリューションは最良のソリューションのように見えますが、待ってください。まだまだあります。C#でプライベートメソッドにアクセスして、我々は、フラグフィールドへの直接アクセスを構築し、int配列を構築避けるために式を使用することができます。

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フィールドに割り当てられます。関数は10進値をrefとして受け取るため、実際のコピーは実行されず、値への参照のみが実行されることに注意してください。DecimalHelperからGetDigits関数を使用するのは簡単です。

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

これは、10進値の小数点以下の桁数を取得するための可能な限り最速の方法です。


3
10進数r =(10進数)-0.01f; そして解決策は失敗します。(このページで見たすべての回答について...):)
GY 2015年

5
注:(Decimal)0.01f全体については、本質的に正確ではない浮動小数点を、Decimalのように非常に構造化されたものにキャストしています。Console.WriteLine((Decimal)0.01f)の出力を見てください。キャストで形成される小数は実際には3桁です。そのため、提供されるすべてのソリューションは2ではなく3と言います。すべてが実際に期待どおりに機能しています。「問題」は、浮動小数点値が正確であると期待していることです。ではない。
ニコリ2015

@Nicholiあなたがそれを理解し0.01、そして0.010正確に等しい数で。さらに、数値データ型に信頼できるある種の「使用桁数」セマンティクスがあるという考えは完全に誤解されています(「許可される桁数」と混同しないでください。表示(の表示)を混同しないでください。特にベースの数値の値は、例えば、値の小数展開は、基礎となる値を持つバイナリ拡張111)で示される繰り返しに、!数字は数字しているわけではなく数字で構成されています
ErikE

6
それらは値が同等ですが、有効数字ではありません。これは、Decimalクラスの大きなユースケースです。文字通り0.010mの桁数を聞いたら、2桁だけだと思いますか?世界中の数学/科学の教師のスコアが最後の0が重要であるとあなたに言うだろうとしても?私たちが言及している問題は、浮動小数点から10進数にキャストすることによって明らかになります。GetBits自体の使用法ではなく、文書化されているとおりに実行されます。有効数字を気にしないのであれば、問題があり、そもそもDecimalクラスを使用すべきではないでしょう。
ニコリ2015

1
@theberserker私が覚えている限り、キャッチはありませんでした-それは両方の方法で機能するはずです。
KristiyanDimitrov19年

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);
}

これは、小数点以下の桁数を見つけるのにどのように役立ちますか?小数をどの文化にも適した文字列に変換しても問題ありません。質問によると、私は小数点以下の桁数を見つけようとしています
Jesse Carter

@JesseCarter:それはあなたがいつでも分割できることを意味します.
オースティンサロネン

@AustinSalonen本当に?InvariantCultureを使用すると、小数点記号としてピリオドが強制的に使用されることに気づいていませんでした
Jesse Carter

以前と同じように、常に価格を。で文字列にキャストします。小数点記号として。しかし、それは私の意見では最もエレガントな方法ではありません...
fixagon 2012年


8

ここのほとんどの人は、decimalが末尾のゼロをストレージと印刷にとって重要であると見なしていることに気付いていないようです。

したがって、0.1m、0.10m、0.100mは同等と比較され、異なる方法で保存され(それぞれ、値/スケール1 / 1、10 / 2、100 / 3として)、それぞれ0.1、0.10、0.100として出力されます。 、によってToString()

そのため、「精度が高すぎる」と報告するソリューションは、実際には正しい精度を報告していますdecimal

さらに、数学ベースのソリューション(10の累乗を掛けるなど)は非常に遅くなる可能性があります(10進数は算術演算の2倍よりも約40倍遅く、浮動小数点で混合することは不正確になる可能性があるためです。 )。同様に、鋳造intまたはlong切り捨ての手段としては(誤りがちであるdecimal-それは96ビット整数の周りに基づいているもののいずれよりもはるかに大きい範囲を有します)。

それほどエレガントではありませんが、精度を得る最も速い方法の1つは次のとおりです(「末尾のゼロを除く小数点以下の桁数」として定義されている場合)。

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;
}

不変の文化は「。」を保証します。小数点として、後続のゼロがトリミングされ、小数点の後に残っている位置がいくつあるかを確認するだけです(小数点がある場合)。

編集:戻り値の型をintに変更


1
@mvmorten戻り値の型をintに変更する必要があると感じた理由がわかりません。バイトは、戻り値をより正確に表します。符号なしおよび狭い範囲(実際には0〜29)。
Zastai

1
反復および計算ベースのソリューションは遅いことに同意します(後続のゼロを考慮しないことを除いて)。ただし、これに文字列を割り当てて操作することも、特にパフォーマンスが重要なコンテキストやGCが遅い場合に、最もパフォーマンスの高い方法ではありません。ポインタロジックを介してスケールにアクセスすることは、かなり速く、割り当てが不要です。
マーティン・ティロ・シュミッツ

はい、スケールの取得ははるかに効率的に行うことができますが、それには後続のゼロが含まれます。そして、それらを削除するには、整数部分で算術演算を行う必要があります。
Zastai

6

また、別の方法として、小数点以下の桁数をカウントするscaleプロパティを持つSqlDecimal型を使用します。10進値をSqlDecimalにキャストしてから、Scaleにアクセスします。

((SqlDecimal)(decimal)yourValue).Scale

1
Microsoftの参照コードを見ると、SqlDecimalにキャストすると内部的に使用されるGetBytesため、安全でないコンテキストでバイトにアクセスする代わりに、バイト配列が割り当てられます。参照コードにはメモとコメントアウトされたコードがあり、その代わりにそれをどのように行うことができるかを示しています。なぜ彼らがそうしなかったのかは私には謎です。このキャストでGCAllocを非表示にするのではなく、これを避けてスケールビットに直接アクセスします。これは、内部で何が行われるかがあまり明確ではないためです。
マーティンティロシュミッツ

4

これまでのところ、リストされているソリューションのほぼすべてがGCメモリを割り当てています。これはC#の方法ですが、パフォーマンスが重要な環境では理想的とは言えません。(useループを割り当てず、末尾のゼロも考慮しないもの。)

したがって、GC Allocを回避するには、安全でないコンテキストでスケールビットにアクセスするだけです。それは壊れやすいように聞こえるかもしれませんが、Microsoftの参照ソースによると、decimalの構造体レイアウトはSequentialであり、フィールドの順序を変更しないようにコメントがあります。

    // 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];
    }
  }
}

バイト配列のGC割り当てやToString変換がないため、これが最もパフォーマンスの高いソリューションになるはずです。Unity2019.1で.Net4.xおよび.Net3.5に対してテストしました。これが失敗するバージョンがある場合は、私に知らせてください。

編集:

安全でないコードの外で同じポインターロジックを実際に実現するために明示的な構造体レイアウトを使用する可能性について私に思い出させてくれた@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;
    }
}

元の問題を解決するには、ほかのすべてのフィールドを取り去り可能性ValueScaleが、多分それはそれらすべてを持っている誰かのために有用である可能性があります。


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、良い点。私もそのアプローチを取り入れました。:)
Martin TiloSchmitz19年

1
注意すべき点の1つは、スケールを0〜28の範囲外に設定すると破損することです。ToString()は機能する傾向がありますが、算術演算は失敗します。
Zastai

もう一度@Zastaiに感謝します、私はそのためのチェックを追加しました:)
Martin

別のこと:ここの何人かの人々は、末尾の小数ゼロを考慮に入れたくありませんでした。を定義const decimal Foo = 1.0000000000000000000000000000m;すると、小数をそれで除算すると、可能な限り最小のスケールに再スケーリングされます(つまり、末尾の小数ゼロは含まれなくなります)。ただし、他の場所で提案した文字列ベースのアプローチよりも高速かどうかを確認するために、これをベンチマークしていません。
Zastai

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;

@ fix-like-codingsは2番目の答えに似ていますが、このようなものについては、再帰を使用するよりも反復アプローチを好みます
Jesse Carter

元の投稿は次のように述べています19.0 should return 1。このソリューションは、常に小数点以下1桁の最小量を想定し、後続のゼロを無視します。10進数は、スケール係数を使用するため、これらを持つことができます。スケールファクターは、Decimal.GetBytes()ポインターロジックから取得した、またはポインターロジックを使用して、配列内のインデックス3を持つ要素のバイト16〜24のようにアクセスできます。
マーティンティロシュミッツ

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);
}

System.Windows.Formsを使用して、Application.CurrentCultureにアクセスすることを忘れないでください


1

あなたが試すことができます:

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

7
小数が整数の場合、これは失敗しませんか?[1]
シルバーマインド2012年

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;
    }

10進入力= 3.376; var instring = input.ToString();

GetDecimalLength(instring)を呼び出す


1
10進値のToString()表現がデータの末尾に「00」を追加するため、これは機能しません。SQLServerのDecimal(12,4)データ型を使用しています。
PeterX 2014年

データをc#タイプのdecimalにキャストして、解決策を試すことができますか?私にとって、C#の10進値でTostring()を使用すると、「00」が表示されません。
スリカーント2014年

1

再帰を使用すると、次のことができます。

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

元の投稿は次のように述べています19.0 should return 1。このソリューションは、後続のゼロを無視します。10進数は、スケール係数を使用するため、これらを持つことができます。スケールファクターは、Decimal.GetBytes()配列内のインデックス3を持つ要素のバイト16〜24のように、またはポインターロジックを使用してアクセスできます。
マーティン・ティロ・シュミッツ


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

を考慮した10進拡張メソッドとして:

  • 異なる文化
  • 整数
  • 負の数
  • 小数点以下の末尾にゼロを設定します(たとえば、1.2300Mは4ではなく2を返します)
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;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.