Calculate System.Decimal Precision and Scale

前端 未结 5 1709
我寻月下人不归
我寻月下人不归 2020-12-01 01:01

Suppose that we have a System.Decimal number.

For illustration, let\'s take one whose ToString() representation is as follows:

d.ToString() = \"123.4         


        
5条回答
  •  孤城傲影
    2020-12-01 01:22

    I do currently have a similar issue, but I do not only need the scale, but also need the mantisse as integer. Based on the solutions above, please find the fastest, I could come up with, below. Statistics: "ViaBits" takes 2,000ms for 7,000,000 checks on my machine. "ViaString" takes 4,000ms for the same task.

        public class DecimalInfo {
    
        public BigInteger Mantisse { get; private set; }
        public SByte Scale { get; private set; }
        private DecimalInfo() {
        }
    
        public static DecimalInfo Get(decimal d) {
            //ViaBits is faster than ViaString.
            return ViaBits(d);
        }
    
        public static DecimalInfo ViaBits(decimal d) {
            //This is the fastest, I can come up with.
            //Tested against the solutions from http://stackoverflow.com/questions/763942/calculate-system-decimal-precision-and-scale
            if (d == 0) {
                return new DecimalInfo() {
                    Mantisse = 0,
                    Scale = 0,
                };
            } else {
                byte scale = (byte)((Decimal.GetBits(d)[3] >> 16) & 31);
                //Calculating the mantisse from the bits 0-2 is slower.
                if (scale > 0) {
                    if ((scale & 1) == 1) {
                        d *= 10m;
                    }
                    if ((scale & 2) == 2) {
                        d *= 100m;
                    }
                    if ((scale & 4) == 4) {
                        d *= 10000m;
                    }
                    if ((scale & 8) == 8) {
                        d *= 100000000m;
                    }
                    if ((scale & 16) == 16) {
                        d *= 10000000000000000m;
                    }
                }
                SByte realScale = (SByte)scale;
                BigInteger scaled = (BigInteger)d;
                //Just for bigger steps, seems reasonable.
                while (scaled % 10000 == 0) {
                    scaled /= 10000;
                    realScale -= 4;
                }
                while (scaled % 10 == 0) {
                    scaled /= 10;
                    realScale--;
                }
                return new DecimalInfo() {
                    Mantisse = scaled,
                    Scale = realScale,
                };
            }
        }
    
        public static DecimalInfo ViaToString(decimal dec) {
            if (dec == 0) {
                return new DecimalInfo() {
                    Mantisse = 0,
                    Scale = 0,
                };
            } else {
                //Is slower than "ViaBits".
                string s = dec.ToString(CultureInfo.InvariantCulture);
    
                int scale = 0;
                int trailingZeros = 0;
                bool inFraction = false;
                foreach (char c in s) {
                    if (inFraction) {
                        if (c == '0') {
                            trailingZeros++;
                        } else {
                            trailingZeros = 0;
                        }
                        scale++;
                    } else {
                        if (c == '.') {
                            inFraction = true;
                        } else if (c != '-') {
                            if (c == '0'){
                                trailingZeros ++;
                            } else {
                                trailingZeros = 0;
                            }
                        }
                    }
                }
    
                if (inFraction) {
                    return new DecimalInfo() {
                        Mantisse = BigInteger.Parse(s.Replace(".", "").Substring(0, s.Length - trailingZeros - 1)),
                        Scale = (SByte)(scale - trailingZeros),
                    };
                } else {
                    return new DecimalInfo() {
                        Mantisse = BigInteger.Parse(s.Substring(0, s.Length - trailingZeros)),
                        Scale = (SByte)(scale - trailingZeros),
                    };
                }
            }
        }
    }
    

提交回复
热议问题