简体   繁体   English

如何在C#中实现健壮的截断方法?

[英]How to implement a robust truncate method in C#?

I know .NET has one built-in but it's an external call. 我知道.NET具有一个内置函数,但这是一个外部调用。 Anyone knows why? 有人知道为什么吗?

But the actual question is how to implement a truncate from scratch where the user will be able to specify how many digits to keep? 但是实际的问题是如何从头开始执行截断,使用户能够指定保留几位数? Is multiplying a number by say 100 and then dividing it by the same amount enough? 将数字乘以100,然后除以相同的数量是否足够? Or is there a better implementation? 还是有更好的实施方案?

Something like: 就像是:

Truncate(12.3456789, 3);
// returns 12.345

You'd probably want to look at IEEE floating-point integers. 您可能希望查看IEEE浮点整数。

You can then use unsafe code to modify the numbers, like: 然后,您可以使用unsafe代码来修改数字,例如:

unsafe
{
    double* pValue = &value;
    var asLong = *(long*)pValue;
    do whatever you want with asLong, e.g. bit-masking it, etc.; 
}

As to the 'why': I have no idea, though the Shared Source CLI may provide clues. 关于“为什么”:我不知道,尽管共享源CLI可能提供了线索。 My guess would be that it might be because of performance optimizations. 我的猜测可能是由于性能优化。

The classic way: 经典方式:

var x = 1.2345678;
var tr = 4;

var truncated = (int) (x * Math.Pow(10, tr)) / Math.Pow(10, tr);

would give 1.2345; 会给1.2345;

Here is how I would do it. 这就是我要怎么做。 In C++, and I think in C# as well, you could get the integer part of a floating point number by casting it to an integer type. 在C ++中,我也认为在C#中,可以通过将其转换为整数类型来获得浮点数的整数部分。

double Truncate (double num, int dig)
{
    if (dig > 15) dig = 15; // Don't overflow
    long p = Math.Pow (10, dig);

    // Save the integer part, so that we don't overflow
    long integer_part = (long)num;

    // Fractional part * 10^dig
    double frac = (num - Convert.ToDouble(integer_part)) * p;
    long frac_trunc = (long)frac;

    // Final result
    double result = Convert.ToDouble(integer_part) + (Convert.ToDouble(frac_trunc) / p);
    return result;
}

Is multiplying a number by say 100 and then dividing it by the same amount enough? 将数字乘以100,然后除以相同的数量是否足够?

That should work, but be careful because with large numbers, or high number of digits, you can easily overflow, and it will give you weird results. 那应该行得通,但是要小心,因为使用大量数字或高位数,您很容易溢出,这将给您带来奇怪的结果。

var result = Math.Round(12.3456789, 3);

圆形数学方法(Double,Int32)

It is not clear the reason you think that Truncate should keep the decmial value. 您认为Truncate应该保留decmial值的原因尚不清楚。

The default method within .NET is described by the following statement: .NET中的默认方法由以下语句描述:

The integral part of d; d的整数部分; that is, the number that remains after any fractional digits have been discarded. 也就是说,丢弃任何小数位后剩余的数字。

It seems like what you want to use is either to format the output string of an double/decmial value and/or use the Math.Round(double, int) function instead. 似乎您想要使用的是格式化双精度或十进制值的输出字符串和/或使用Math.Round(double,int)函数代替。

You could just use: 您可以使用:

double num = 2.22939393; num  = Convert.ToDouble(num.ToString("#0.000")); 

From one of the duplicate questions: 从重复的问题之一:

public static decimal TruncateToDecimalPlace(this decimal numberToTruncate, int decimalPlaces) 
{     
       decimal power = (decimal)(Math.Pow(10.0, (double)decimalPlaces));      
       return Math.Truncate((power * numberToTruncate)) / power; 
} 

I understand this still uses the Truncate method. 我了解这仍然使用Truncate方法。 I only provided this code since you wanted a Truncate method that would keep the decmial value of a number and the default built-in Truncate method does not. 我仅提供此代码是因为您想要一个Truncate方法,该方法将保留数字的decmial值,而默认的内置Truncate方法则不保留。

You could always just use this: 您始终可以使用以下命令:

Math.Round does NOT call the SplitFractionDouble from what I can tell 我可以告诉Math.Round不会调用SplitFractionDouble

     private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) {
            if (Abs(value) < doubleRoundLimit) {
                Double power10 = roundPower10Double[digits];
                value *= power10;
                if (mode == MidpointRounding.AwayFromZero) {                
                    double fraction = SplitFractionDouble(&value); 
                    if (Abs(fraction) >= 0.5d) {
                        value += Sign(fraction);
                    }
                }
                else {
                    // On X86 this can be inlined to just a few instructions
                    value = Round(value);
                }
                value /= power10;
            }
            return value;
          }           


     public static double Round(double value, int digits)
      {
           if ((digits < 0) || (digits > maxRoundingDigits))
               throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
           return InternalRound(value, digits, MidpointRounding.ToEven);                     
      }

  public static double Round(double value, MidpointRounding mode) {
         return Round(value, 0, mode);
      }

      public static double Round(double value, int digits, MidpointRounding mode) {
          if ((digits < 0) || (digits > maxRoundingDigits))
              throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
          if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) {            
              throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", mode, "MidpointRounding"), "mode");
          }
          return InternalRound(value, digits, mode);                           
      }

      public static Decimal Round(Decimal d) {
        return Decimal.Round(d,0);
      }

      public static Decimal Round(Decimal d, int decimals) {
        return Decimal.Round(d,decimals);
      }

      public static Decimal Round(Decimal d, MidpointRounding mode) {
        return Decimal.Round(d, 0, mode);
      }

      public static Decimal Round(Decimal d, int decimals, MidpointRounding mode) {
        return Decimal.Round(d, decimals, mode);
      }


public static Decimal Floor(Decimal d) { return Decimal.Floor(d); public static Decimal Floor(Decimal d){return Decimal.Floor(d); } }

[MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern double Floor(double d); [MethodImplAttribute(MethodImplOptions.InternalCall)]公共静态外部double Floor(double d);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM