丸めずに小数点以下2桁を切り捨て


107

3.4679の値があり、3.46が必要だとしましょう。切り上げずに、小数点以下2桁に切り捨てるにはどうすればよいですか?

私は以下を試しましたが、3つすべてが3.47を私に与えます:

void Main()
{
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.ToEven));
    Console.Write(Math.Round(3.4679, 2,MidpointRounding.AwayFromZero));
    Console.Write(Math.Round(3.4679, 2));
}

これは3.46を返しますが、どうやら汚いようです:

void Main()
{
    Console.Write(Math.Round(3.46799999999 -.005 , 2));
}
c#  math  rounding 

回答:


150
value = Math.Truncate(100 * value) / 100;

このような分数は浮動小数点で正確に表現できないことに注意してください。


12
値には小数を使用してください。この答えは機能します。浮動小数点表現で常に機能することはほとんどありません。
driis

1
それで、浮動小数点リテラルで丸め方向を指定できるかどうか疑問に思います。うーん。
Steve314

なければならないいくつかの数が308の以上の数字を格納できるという仮定で計算することであることをプログラマが指示する方法著しく不適当。Doubleは15のみを格納できます。オーバーフローはここでの機能であり、かなりひどくオーバーフローしました。
ハンスパッサント2016年

すみません、「値」は10進数だと思いました。
ナイトコーダー、2016年

54

C#で小数を切り捨てる実際の使用法のための完全な関数があると、より便利です。これは、必要に応じて非常に簡単にDecimal拡張メソッドに変換できます。

public decimal TruncateDecimal(decimal value, int precision)
{
    decimal step = (decimal)Math.Pow(10, precision);
    decimal tmp = Math.Truncate(step * value);
    return tmp / step;
}

VB.NETが必要な場合は、これを試してください。

Function TruncateDecimal(value As Decimal, precision As Integer) As Decimal
    Dim stepper As Decimal = Math.Pow(10, precision)
    Dim tmp As Decimal = Math.Truncate(stepper * value)
    Return tmp / stepper
End Function

次に、次のように使用します。

decimal result = TruncateDecimal(0.275, 2);

または

Dim result As Decimal = TruncateDecimal(0.275, 2)

1
これは大きな数でオーバーフローします。
ナイトコーダー、

1
ナイトコーダーに追加すると、関数の中間としてInt32を使用しているという事実により、オーバーフローが発生します。Int64をIntegerにキャストする必要がある場合は、Int64を使用する必要があります。とにかくTruncateが10進積分を返すので、とにかく余分なオーバーヘッドが発生するのはなぜでしょうか。次のようにしてください:decimal step =(decimal)Math.Pow(10、precision); Math.Truncate(step * value)/ step;を返します。
Sarel Esterhuizen

キャストを整数に落としました。読みやすくし、関数がどのように機能するかを理解するために、それらを別々の行に残しました。
Corgalore 2017

27

モジュラス演算子を使用します。

var fourPlaces = 0.5485M;
var twoPlaces = fourPlaces - (fourPlaces % 0.01M);

結果:0.54


1
私はこれらすべての他の空想的な解決策を理解していません(読んで:検証に時間をかけませんでした)。これはまさに私が探していたものです。ありがとうございました!
Isaac Baker

ネットフィドルの上でこれを実行するclickyすることは生産0.5400... D.ネステロフによって答えは以下の予想生産します0.54
ttugates

@ttugatesは、0.54と0.5400がまったく同じ値であることを知っていますか?:適切にフォーマットされた場合、それはそれはのためのフォーマットに時間が来るまで/しない限り続くどのように多くのゼロ問題ではありません表示で、その場合、結果は同じになります$"{0.54m:C}"生成"$0.54"とはい、 $"{0.5400m:C}"生産します"$0.54"
レナードルイス

25

ユニバーサルで高速な方法(Math.Pow()/乗算なし)System.Decimal

decimal Truncate(decimal d, byte decimals)
{
    decimal r = Math.Round(d, decimals);

    if (d > 0 && r > d)
    {
        return r - new decimal(1, 0, 0, false, decimals);
    }
    else if (d < 0 && r < d)
    {
        return r + new decimal(1, 0, 0, false, decimals);
    }

    return r;
}

4
私は他の回答で述べられているすべてのテストを通してこれを実行しました、そしてそれは完全に機能します。驚いたことに、これ以上の投票はありません。小数は0から28までしか指定できないことに注意してください(おそらくほとんどの人には問題ありません)。
RichardOD 2018年

1
同感です。これが最良の答えです。+1
ブランコディミトリエビッチ2018

1
すばらしい答えです。これを「箱の外で考える」と
言い

23

他の例の1つの問題は、入力値を乗算してから除算することです。ここでは、最初に乗算することで10進数をオーバーフローできるエッジケースがあります。エッジケースですが、私が遭遇したものです。以下のように、小数部分を個別に処理する方が安全です。

    public static decimal TruncateDecimal(this decimal value, int decimalPlaces)
    {
        decimal integralValue = Math.Truncate(value);

        decimal fraction = value - integralValue;

        decimal factor = (decimal)Math.Pow(10, decimalPlaces);

        decimal truncatedFraction = Math.Truncate(fraction * factor) / factor;

        decimal result = integralValue + truncatedFraction;

        return result;
    }

私はこれが古いことを知っていますが、これに気づき、問題を抱えています。ここにある要素はintなので、小数点以下の桁数が多い場合(たとえば25)は、最終結果に精度エラーが発生します。因子タイプを10進数に変更することで修正しました。
TheKingDave 2013

@TheKingDave:おそらく無関係ですが、係数に小数を含めることはできないので、それをできるだけ長くモデル化する方が良いでしょうか?
Ignacio Soler Garcia

@SoMoS私にとって、Decimalは、factorのストレージ値が最も高いので、うまく機能しました。まだ制限がありますが、私のアプリケーションには十分な大きさです。一方、長いと私のアプリケーションに十分な数を保存することができませんでした。たとえば、Truncate(25)をlongで実行すると、不正確になることがあります。
TheKingDave 2013

@TheKingDaveの提案に従って、より多くの場所への切り捨てができるように更新されました。ありがとうございます。
ティムロイド

6

10進数の解は残しておきます。

ここでの小数の解の一部はオーバーフローする傾向があります(非常に大きな10進数を渡してメソッドがそれを乗算しようとすると)。

Tim Lloydのソリューションはオーバーフローから保護されていますが、速すぎません。

次の解決策は約2倍高速で、オーバーフローの問題はありません。

public static class DecimalExtensions
{
    public static decimal TruncateEx(this decimal value, int decimalPlaces)
    {
        if (decimalPlaces < 0)
            throw new ArgumentException("decimalPlaces must be greater than or equal to 0.");

        var modifier = Convert.ToDecimal(0.5 / Math.Pow(10, decimalPlaces));
        return Math.Round(value >= 0 ? value - modifier : value + modifier, decimalPlaces);
    }
}

[Test]
public void FastDecimalTruncateTest()
{
    Assert.AreEqual(-1.12m, -1.129m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.120m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.125m. TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.1255m.TruncateEx(2));
    Assert.AreEqual(-1.12m, -1.1254m.TruncateEx(2));
    Assert.AreEqual(0m,      0.0001m.TruncateEx(3));
    Assert.AreEqual(0m,     -0.0001m.TruncateEx(3));
    Assert.AreEqual(0m,     -0.0000m.TruncateEx(3));
    Assert.AreEqual(0m,      0.0000m.TruncateEx(3));
    Assert.AreEqual(1.1m,    1.12m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.15m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.19m.  TruncateEx(1));
    Assert.AreEqual(1.1m,    1.111m. TruncateEx(1));
    Assert.AreEqual(1.1m,    1.199m. TruncateEx(1));
    Assert.AreEqual(1.2m,    1.2m.   TruncateEx(1));
    Assert.AreEqual(0.1m,    0.14m.  TruncateEx(1));
    Assert.AreEqual(0,      -0.05m.  TruncateEx(1));
    Assert.AreEqual(0,      -0.049m. TruncateEx(1));
    Assert.AreEqual(0,      -0.051m. TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.14m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.15m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.16m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.19m.  TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.199m. TruncateEx(1));
    Assert.AreEqual(-0.1m,  -0.101m. TruncateEx(1));
    Assert.AreEqual(0m,     -0.099m. TruncateEx(1));
    Assert.AreEqual(0m,     -0.001m. TruncateEx(1));
    Assert.AreEqual(1m,      1.99m.  TruncateEx(0));
    Assert.AreEqual(1m,      1.01m.  TruncateEx(0));
    Assert.AreEqual(-1m,    -1.99m.  TruncateEx(0));
    Assert.AreEqual(-1m,    -1.01m.  TruncateEx(0));
}

2
"Ex"を後ろに付けるのは好きではありません。C#はオーバーロードをサポートし、Truncateメソッドは.netネイティブメソッドと一緒にグループ化され、ユーザーにシームレスなエクスペリエンスを提供します。
Gqqnbig 2016年

1
アルゴリズムの結果、正しくない結果が生じます。デフォルトのMidpointRoundingモードはBanker's Roundingで、0.5を最も近い偶数値に丸めます。 Assert.AreEqual(1.1m, 1.12m.TruncateEx(1));これが原因で失敗します。Assert.AreEqual(0m, 0m.TruncateEx(1));
Math.Round

1
あなたが使用する場合は、このソリューションの意志の仕事がある唯一の方法はMidpointRounding.AwayFromZero、具体的に値0処理するコード
ジョンSenchyna

1
Jonは正解です。0が明示的に処理されない限り、0m.TruncateEx(0)は-1になります。同様に、MidpointRounding.AwayFromZeroがMath.Round内で使用されない限り、-11m.TruncateEx(0)は-10になります。しかし、それらの変更でうまく機能するようです。
Ho Ho Ho

1
AwayFromZeroが変更され、0が明示的に処理されている場合でも、-9999999999999999999999999999m.TruncateEx(0)は-9999999999999999999999999998となるため、場合によっては依然として誤りがあります。
Ho Ho Ho

3

これは古い質問ですが、多くのanwserはパフォーマンスがよくない、または大きな数字に対してオーバーフローします。D. Nesterovの答えが最良だと思います。堅牢で、シンプルで、高速です。2セントを足したいだけです。小数で遊んだりソースコードをチェックアウトしたりしました。public Decimal (int lo, int mid, int hi, bool isNegative, byte scale) コンストラクタのドキュメントから。

10進数の2進数表現は、1ビットの符号、96ビットの整数、および整数を除算し、その小数部分を指定するために使用されるスケーリング係数で構成されます。スケーリング係数は、暗黙的に10を0から28の範囲の指数に累乗したものです。

これを知って、私の最初のアプローチはdecimal、切り捨てたい小数に対応するスケールを持つ別のものを作成し、それを切り捨てて、最終的に希望のスケールで小数を作成することでした。

private const int ScaleMask = 0x00FF0000;
    public static Decimal Truncate(decimal target, byte decimalPlaces)
    {
        var bits = Decimal.GetBits(target);
        var scale = (byte)((bits[3] & (ScaleMask)) >> 16);

        if (scale <= decimalPlaces)
            return target;

        var temporalDecimal = new Decimal(bits[0], bits[1], bits[2], target < 0, (byte)(scale - decimalPlaces));
        temporalDecimal = Math.Truncate(temporalDecimal);

        bits = Decimal.GetBits(temporalDecimal);
        return new Decimal(bits[0], bits[1], bits[2], target < 0, decimalPlaces);
    }

この方法はD. Nesterovの方法より速くなく、より複雑なので、もう少し遊んでみました。私の推測では、助剤を作成しdecimalてビットを2回取得する必要があるため、処理が遅くなると考えられます。2回目の試行では、Decimal.GetBits(Decimal d)メソッドによって返されたコンポーネントを自分で操作しました。アイデアは、コンポーネントを必要な数の10倍に分割し、スケールを縮小することです。このコードは、(大きく)Decimal.InternalRoundFromZero(ref Decimal d、int decimalCount)メソッドに基づいています。

private const Int32 MaxInt32Scale = 9;
private const int ScaleMask = 0x00FF0000;
    private const int SignMask = unchecked((int)0x80000000);
    // Fast access for 10^n where n is 0-9        
    private static UInt32[] Powers10 = new UInt32[] {
        1,
        10,
        100,
        1000,
        10000,
        100000,
        1000000,
        10000000,
        100000000,
        1000000000
    };

    public static Decimal Truncate(decimal target, byte decimalPlaces)
    {
        var bits = Decimal.GetBits(target);
        int lo = bits[0];
        int mid = bits[1];
        int hi = bits[2];
        int flags = bits[3];

        var scale = (byte)((flags & (ScaleMask)) >> 16);
        int scaleDifference = scale - decimalPlaces;
        if (scaleDifference <= 0)
            return target;

        // Divide the value by 10^scaleDifference
        UInt32 lastDivisor;
        do
        {
            Int32 diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference;
            lastDivisor = Powers10[diffChunk];
            InternalDivRemUInt32(ref lo, ref mid, ref hi, lastDivisor);
            scaleDifference -= diffChunk;
        } while (scaleDifference > 0);


        return new Decimal(lo, mid, hi, (flags & SignMask)!=0, decimalPlaces);
    }
    private static UInt32 InternalDivRemUInt32(ref int lo, ref int mid, ref int hi, UInt32 divisor)
    {
        UInt32 remainder = 0;
        UInt64 n;
        if (hi != 0)
        {
            n = ((UInt32)hi);
            hi = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        if (mid != 0 || remainder != 0)
        {
            n = ((UInt64)remainder << 32) | (UInt32)mid;
            mid = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        if (lo != 0 || remainder != 0)
        {
            n = ((UInt64)remainder << 32) | (UInt32)lo;
            lo = (Int32)((UInt32)(n / divisor));
            remainder = (UInt32)(n % divisor);
        }
        return remainder;
    }

私は厳密なパフォーマンステストを実行していませんが、MacOS Sierra 10.12.6、3,06 GHzのIntel Core i3プロセッサで.NetCore 2.1をターゲットにしている場合、この方法はD. Nesterovの方法よりもはるかに高速であるように見えます(それ以降、数値は示しません) 、私が述べたように、私のテストは厳密ではありません)。パフォーマンスの向上がコードの複雑さの増加に見合うかどうかを評価するのは、これを実装する人次第です。


私はすべての考えと努力のために賛成票を投じなければなりませんでした。あなたはNesterov'sをベンチマークとして設定し続けました-帽子は脱ぎました。
AndrewBenjamin



1

拡張メソッドは次のとおりです。

public static decimal? TruncateDecimalPlaces(this decimal? value, int places)
    {
        if (value == null)
        {
            return null;
        }

        return Math.Floor((decimal)value * (decimal)Math.Pow(10, places)) / (decimal)Math.Pow(10, places);

    } // end

0

パフォーマンスについてあまり気にせず、最終結果が文字列になる可能性がある場合、次のアプローチは浮動精度の問題に耐性があります。

string Truncate(double value, int precision)
{
    if (precision < 0)
    {
        throw new ArgumentOutOfRangeException("Precision cannot be less than zero");
    }

    string result = value.ToString();

    int dot = result.IndexOf('.');
    if (dot < 0)
    {
        return result;
    }

    int newLength = dot + precision + 1;

    if (newLength == dot + 1)
    {
        newLength--;
    }

    if (newLength > result.Length)
    {
        newLength = result.Length;
    }

    return result.Substring(0, newLength);
}

6
実際、「。」をハードコーディングしています。良いアイデアではありません。System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]を使用することをお勧めします
David Airapetyan 2013

0

ここにTRUNC関数の私の実装があります

private static object Tranc(List<Expression.Expression> p)
{
    var target = (decimal)p[0].Evaluate();

    // check if formula contains only one argument
    var digits = p.Count > 1
        ? (decimal) p[1].Evaluate()
        : 0;

    return Math.Truncate((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits);
}

0

これはどうですか?

Function TruncateDecimal2(MyValue As Decimal) As Decimal
        Try
            Return Math.Truncate(100 * MyValue) / 100
        Catch ex As Exception
            Return Math.Round(MyValue, 2)
        End Try
End Function

0

上記の解決策とは別に、私たちが達成できる別の方法があります。

    decimal val=23.5678m,finalValue;

    //take the decimal part    
     int decimalPos = val.ToString().IndexOf('.');
     string decimalPart = val.ToString().Substring(decimalPosition+1,val.ToString().Length);
    //will result.56
   string wholePart=val.ToString().Substring(0,decimalPos-1);
   //concantinate and parse for decimal.
  string truncatedValue=wholePart+decimalPart;//"23.56"
  bool isDecimal=Decimal.tryParse(truncatedValue,out finalValue);//finalValue=23.56

0

状況によってはこれで十分な場合があります。

| SubCent:0.010000 |にフォーマットする傾向があるSubCent = 0.0099999999999999999999999999999Mの 10進数値が ありました via および他の多くのフォーマットの選択肢。string.Format("{0:N6}", SubCent );

私の要件は、SubCent値を丸めることではなく、すべての桁を記録することではありませんでした。

以下は私の要件を満たしました:

string.Format("SubCent:{0}|", 
    SubCent.ToString("N10", CultureInfo.InvariantCulture).Substring(0, 9));

これは文字列を返します:| SubCent:0.0099999 |

整数部分を持つ値に対応するために、以下が始まりです。

tmpValFmt = 567890.0099999933999229999999M.ToString("0.0000000000000000000000000000");
decPt = tmpValFmt.LastIndexOf(".");
if (decPt < 0) decPt = 0;
valFmt4 = string.Format("{0}", tmpValFmt.Substring(0, decPt + 9));

これは文字列を返します:

valFmt4 = "567890.00999999"

0

この関数を使用して、文字列変数の10進数の後の値を切り捨てています

public static string TruncateFunction(string value)
    {
        if (string.IsNullOrEmpty(value)) return "";
        else
        {
            string[] split = value.Split('.');
            if (split.Length > 0)
            {
                string predecimal = split[0];
                string postdecimal = split[1];
                postdecimal = postdecimal.Length > 6 ? postdecimal.Substring(0, 6) : postdecimal;
                return predecimal + "." + postdecimal;

            }
            else return value;
        }
    }

1
このコードは質問に答えることがありますが、問題を解決する方法および/または理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。
Nic3500 2018

0

これは私がやったことです:

        c1 = a1 - b1;
        d1 = Math.Ceiling(c1 * 100) / 100;

小数点以下を切り上げたり切り下げたりせずに、入力された2つの数値を減算します。他の解決策は私のために機能しないので。それが他の人のために働くかどうかわからない、私はこれを共有したいだけです:)それが私のような問題の解決策を見つけている人のために機能することを願っています。ありがとう

PS:私は初心者なので、これについて何か指摘しても構いません。:Dこれは、実際にお金を扱っている場合、セントの原因である場合に適しています。小数点以下2桁しかなく、四捨五入はできません。


0
        public static void ReminderDigints(decimal? number, out decimal? Value,  out decimal? Reminder)
        {
            Reminder = null;
            Value = null;
            if (number.HasValue)
            {
                Value = Math.Floor(number.Value);
                Reminder = (number - Math.Truncate(number.Value));
            }
        }



        decimal? number= 50.55m;             
        ReminderDigints(number, out decimal? Value, out decimal? Reminder);

0
public static decimal TruncateDecimalPlaces(this decimal value, int precision)
    {
        try
        {
            step = (decimal)Math.Pow(10, precision);
            decimal tmp = Math.Truncate(step * value);
            return tmp / step;
        }
        catch (OverflowException)
        {
            step = (decimal)Math.Pow(10, -1 * precision);
            return value - (value % step);
        }
    }

-2

実際には、3.4679から3.46が必要です。これは単なる文字の表現であるため、数学関数とは関係ありません。数学関数はこの作業を行うことを目的としていません。次のコードを使用してください。

Dim str1 As String
str1=""
str1 ="3.4679" 
  Dim substring As String = str1.Substring(0, 3)

    ' Write the results to the screen.
    Console.WriteLine("Substring: {0}", substring)

Or 
    Please use the following code.
Public function result(ByVal x1 As Double) As String 
  Dim i as  Int32
  i=0
  Dim y as String
  y = ""
  For Each ch as Char In x1.ToString
    If i>3 then
     Exit For
    Else
    y + y +ch
    End if
    i=i+1
  Next
  return y
End Function

上記のコードは任意の数値に変更できますボタンのクリックイベントに次のコードを挿入します

Dim str As String 
str= result(3.4679)
 MsgBox("The number is " & str)

-2

どうですか

var i = Math.Truncate(number);var r = i + Math.Truncate((number - i) * 100) / 100;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.