Programing

반올림하지 않고 소수점 두 자리 자르기

lottogame 2020. 8. 29. 11:52
반응형

반올림하지 않고 소수점 두 자리 자르기


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

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)

다른 예제의 한 가지 문제는 입력 값 나누기 전에 곱한다는 것입니다. 여기에는 엣지 케이스를 먼저 곱하여 십진수를 넘칠 수있는 엣지 케이스가 있습니다.하지만 제가 본 것입니다. 다음과 같이 분수 부분을 개별적으로 처리하는 것이 더 안전합니다.

    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


다음에 대한 보편적이고 빠른 방법 ( 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;
}

나는 십진수에 대한 해결책을 남길 것입니다.

여기서 소수에 대한 일부 솔루션은 오버플로가 발생하기 쉽습니다 (매우 큰 소수를 전달하고 메서드가 곱하려고 시도하는 경우).

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

이것이 당신을 위해 일할까요?

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

이것은 오래된 질문이지만 많은 anwsers가 잘 수행되지 않거나 큰 숫자에 대해 오버플로됩니다. 나는 D. Nesterov 대답이 가장 좋은 대답이라고 생각합니다. 강력하고 간단하며 빠릅니다. 2 센트 만 더하고 싶습니다. 나는 소수 를 가지고 놀았고 또한 소스 코드를 확인했다 . 로부터 public Decimal (int lo, int mid, int hi, bool isNegative, byte scale) 생성자 문서 .

Decimal 숫자의 이진 표현은 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비트를 두 번 검색 해야하는 것이 속도가 느려진다는 것입니다. 두 번째 시도에서 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,06GHz Intel Core i3 프로세서 및 .NetCore 2.1을 대상으로하는이 방법은 D. Nesterov보다 훨씬 빠른 것 같습니다. , 내가 언급했듯이 내 테스트는 엄격하지 않습니다). 추가 된 코드 복잡성에 대해 성능 향상이 보상을 받는지 여부를 평가하는 것은이를 구현하는 사람에게 달려 있습니다.


성능에 대해 너무 걱정하지 않고 최종 결과가 문자열이 될 수있는 경우 다음 접근 방식은 부동 정밀도 문제에 탄력적입니다.

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

다음은 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);
}

이건 어때?

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

위의 솔루션 외에도 우리가 달성 할 수있는 또 다른 방법이 있습니다.

    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

일부 조건에서는 이것으로 충분할 수 있습니다.

I had a decimal value of SubCent = 0.0099999999999999999999999999M that tends to format to |SubCent:0.010000| via string.Format("{0:N6}", SubCent ); and many other formatting choices.

My requirement was not to round the SubCent value, but not log every digit either.

The following met my requirement:

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

Which returns the string : |SubCent:0.0099999|

To accommodate the value having an integer part the following is a start.

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

Which returns the string :

valFmt4 = "567890.00999999"

i am using this function to truncate value after decimal in a string variable

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

This is what i did:

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

subtracting two inputted numbers without rounding up or down the decimals. because the other solutions does not work for me. don't know if it will work for others, i just want to share this :) Hope it works tho for those who's finding solution to a problem similar to mine. Thanks

PS: i'm a beginner so feel free to point out something on this. :D this is good if you're actually dealing with money, cause of the cents right? it only have 2 decimal places and rounding it is a no no.


Actually you want 3.46 from 3.4679 . This is only representation of characters.So there is nothing to do with math function.Math function is not intended to do this work. Simply use the following code.

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

The above code can be modified for any numbers Put the following code in a button click event

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

참고URL : https://stackoverflow.com/questions/3143657/truncate-two-decimal-places-without-rounding

반응형