値が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));
}
value = Math.Truncate(100 * value) / 100;
これらの分数は浮動小数点で正確に表現できないことに注意してください。
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つの問題は、入力値を乗算することですbeforeそれを除算します。ここには、最初に乗算することで10進数をオーバーフローさせることができるEdgeケースがありますが、Edgeケースですが、私が遭遇したことです。次のように小数部分を個別に処理する方が安全です。
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;
}
モジュラス演算子を使用します。
var fourPlaces = 0.5485M;
var twoPlaces = fourPlaces - (fourPlaces % 0.01M);
結果:0.54
System.Decimal
の汎用かつ高速な方法(Math.Pow()
/乗算なし):
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;
}
10進数のソリューションはそのままにします。
ここでの小数の解決策の一部は、オーバーフローする傾向があります(非常に大きな10進数を渡すと、メソッドはそれを乗算しようとします)。
ティムロイドのソリューションはオーバーフローから保護されていますが、高速ではありません。
次の解決策は約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));
}
これはあなたのために働くでしょうか?
Console.Write(((int)(3.4679999999*100))/100.0);
((long)(3.4679 * 100)) / 100.0
はあなたが欲しいものを与えるでしょうか?
拡張メソッドは次のとおりです。
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
これはどうですか?
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
状況によっては、これで十分な場合があります。
SubCent = 0.00999999999999999999999999999999Mの10進数値がありましたが、これはstring.Format("{0:N6}", SubCent );
および他の多くのフォーマット選択を介してSubCent:0.010000にフォーマットする傾向があります。
私の要件は、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"
これは私がやったことです:
c1 = a1 - b1;
d1 = Math.Ceiling(c1 * 100) / 100;
小数を切り上げまたは切り捨てることなく、2つの入力された数値を減算します。他のソリューションは私のために機能しないためです。それが他の人のために働くかどうかわからない、私はこれを共有したいだけです:)それは私のものと同様の問題の解決策を見つけている人のためにカントーに働くことを望みます。ありがとう
PS:私は初心者ですので、これについて何かを指摘してください。 :D実際にお金を扱っている場合、これは良いです、セントの原因は正しいですか?小数点以下2桁しかないため、丸めることはできません。
ここに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);
}
これは古い質問ですが、多くのアンサーは、大きな数字に対してうまく機能しないか、オーバーフローしません。 D. Nesterovの答えは最高のものだと思います:堅牢、シンプル、高速。 2セントを追加したいだけです。 decimals で遊んだり、 source code もチェックアウトした。 public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)
constructor documentation から。
10進数の2進表現は、1ビットの符号、96ビットの整数、整数の除算に使用するスケーリング係数で構成され、その小数部分を指定します。スケーリング係数は、暗黙的に0から28の範囲の指数に引き上げられた数値10です。
これを知って、私の最初のアプローチは、スケールが破棄したい小数に対応する別の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)method 自分で返されたコンポーネントを操作しました。アイデアは、必要に応じてコンポーネントを10倍に分割し、規模を縮小することです。コードは Decimal.InternalRoundFromZero(ref Decimal d、int decimalCount)method に基づいています。
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の方法よりもはるかに高速であるようです、私が言ったように、私のテストは厳密ではありません)。パフォーマンスの向上が、コードの複雑さの増加に見合うかどうかを評価するのは、これを実装する人次第です。
私はこの関数を使用して、文字列変数の小数点以下の値を切り捨てています
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;
}
}
上記のソリューションとは別に、達成できる別の方法があります。
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
パフォーマンスについてあまり心配せず、最終結果が文字列である場合、次のアプローチは浮動精度の問題に対して回復力があります。
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);
}