截断两个小数位而不进行舍入

时间:2010-06-29 18:32:05

标签: c# math rounding

假设我的值为3.4679且想要3.46,如何在没有四舍五入的情况下截断到两位小数?

我尝试了以下但是三个都给了我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));
}

23 个答案:

答案 0 :(得分:117)

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

请注意,这些分数无法以浮点精确表示。

答案 1 :(得分:48)

在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)

答案 2 :(得分:22)

其他示例的一个问题是它们在除以之前将输入值相乘。这里有一个边缘情况,你可以通过乘以第一个边缘情况来溢出十进制,但是我遇到了一些问题。分别如下处理小数部分更安全:

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

答案 3 :(得分:18)

使用模数运算符:

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

结果:0.54

答案 4 :(得分:15)

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

答案 5 :(得分:6)

我将保留十进制数字的解决方案。

这里的一些小数位解决方案容易溢出(如果我们传递一个非常大的十进制数,并且该方法会尝试将其相乘)。

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

答案 6 :(得分:2)

这对你有用吗?

Console.Write(((int)(3.4679999999*100))/100.0);

答案 7 :(得分:2)

在 .NET Core 3.1 及更高版本中,Math.RoundDecimal.Round 可以通过新的 MidpointRounding.ToNegativeInfinity 模式截断数字。

这些行:

Console.WriteLine(Math.Round(3.4679, 2,MidpointRounding.ToNegativeInfinity));
Console.WriteLine(Math.Round(3.9999, 2,MidpointRounding.ToNegativeInfinity));

制作:

3.46
3.99

要处理负数,应使用 MidpointRounding.ToPositiveInfinity

一种方法可用于处理正数和负数:

public static Truncate(Decimal d,int decimals)
{
    var mode=d>0
       ? MidpointRounding.ToNegativeInfinity 
       : MidpointRounding.ToPositiveInfinity);
    return Math.Round(d,decimals,mode);
}

public static Truncate(Double d,int decimals)
{
    var mode=d>0
       ? MidpointRounding.ToNegativeInfinity 
       : MidpointRounding.ToPositiveInfinity);
    return Math.Round(d,decimals,mode);
}

答案 8 :(得分:2)

((long)(3.4679 * 100)) / 100.0会给你想要的东西吗?

答案 9 :(得分: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

答案 10 :(得分: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);
}

答案 11 :(得分: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

答案 12 :(得分: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

答案 13 :(得分: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);
}

答案 14 :(得分:0)

在某些情况下,这可能就足够了。

我的小数值为 SubCent = 0.009999999999999999999999999999 ,可能会通过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"

答案 15 :(得分:0)

我正在使用此函数截断字符串变量中小数点后的值

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

答案 16 :(得分:0)

这是一个古老的问题,但是许多选拔器的性能都不佳,或者由于数量过多而溢出。我认为D. Nesterov的答案是最好的答案:健壮,简单且快速。我只想加两分钱。 我玩过decimals,还检查了source code。来自public Decimal (int lo, int mid, int hi, bool isNegative, byte scale) constructor documentation

  

十进制数的二进制表示形式由一个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并两次检索这些位,这会使它变慢。第二次尝试时,我自己操作了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的要快得多(我不会给出数字,因为如上所述(我的测试不严格)。实施此方法的人员取决于评估是否为增加的代码复杂性而获得了性能提升。

答案 17 :(得分:0)

这就是我所做的:

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

减去两个输入数字而不舍入或舍去小数位。 因为其他解决方案对我不起作用。 不知道它是否对其他人有用,我只想分享这个:) 希望它对那些寻找与我类似的问题的解决方案的人有用。谢谢

PS:我是一个初学者,请随时指出一些有关此的内容。 :D 如果您实际上在处理金钱,这很好,是因为有钱吗?它只有两位小数,四舍五入是一个否号。

答案 18 :(得分: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);
        }
    }

答案 19 :(得分:0)

我最喜欢的是

var myvalue = 54.301012345;
var valueiwant = myvalue.toString("0.00");
//result => "54.30"

//additional
var valueiwant2 = myvalue.toString("0.##");
//result => "54.3" // without zero

答案 20 :(得分:-1)

关于

var i = Math.Truncate(number);

var r = i + Math.Truncate((number - i) * 100) / 100;

答案 21 :(得分:-1)

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

答案 22 :(得分:-2)

实际上你想从3.4679获得3.46。 这只是字符的表示。所以与数学函数没有任何关系.Math函数不是为了完成这项工作。 只需使用以下代码即可。

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)