在 C# 中实现银行家舍入法

在 C# 中实现银行家舍入法(四舍六入五成双)有几种方式。以下是完整的实现方案:

方法一:使用内置的 Math.Round(推荐)

C# 内置的 Math.Round 方法默认就使用银行家舍入法:

using System;

public class BankersRoundingExample
{
    public static void Main()
    {
        // 测试数据
        double[] testNumbers = { 1.235, 1.245, 1.255, 1.265, 1.275, 1.285 };
        
        Console.WriteLine("银行家舍入法示例(保留2位小数):");
        foreach (double num in testNumbers)
        {
            double rounded = Math.Round(num, 2);
            Console.WriteLine($"{num:F3} -> {rounded:F2}");
        }
        
        // 显式指定舍入规则
        Console.WriteLine("\n显式指定舍入规则:");
        double test1 = 1.235;
        double test2 = 1.245;
        
        Console.WriteLine($"{test1:F3} -> {Math.Round(test1, 2, MidpointRounding.ToEven):F2}");
        Console.WriteLine($"{test2:F3} -> {Math.Round(test2, 2, MidpointRounding.ToEven):F2}");
        
        // 对比传统四舍五入
        Console.WriteLine("\n对比传统四舍五入:");
        Console.WriteLine($"{test1:F3} -> 银行家: {Math.Round(test1, 2, MidpointRounding.ToEven):F2}, " +
                         $"传统: {Math.Round(test1, 2, MidpointRounding.AwayFromZero):F2}");
    }
}

方法二:自定义实现

如果需要完全控制舍入逻辑,可以自定义实现:

using System;

public static class BankersRounding
{
    /// <summary>
    /// 自定义银行家舍入法实现
    /// </summary>
    /// <param name="value">要舍入的值</param>
    /// <param name="decimals">小数位数</param>
    /// <returns>舍入后的值</returns>
    public static double CustomBankersRound(double value, int decimals)
    {
        if (decimals < 0)
            throw new ArgumentException("小数位数不能为负数", nameof(decimals));
        
        double factor = Math.Pow(10, decimals);
        double scaledValue = value * factor;
        
        // 分离整数和小数部分
        double integerPart = Math.Truncate(scaledValue);
        double fractionalPart = scaledValue - integerPart;
        
        // 应用银行家舍入规则
        if (Math.Abs(fractionalPart) < 0.5)
        {
            // 四舍
            return integerPart / factor;
        }
        else if (Math.Abs(fractionalPart) > 0.5)
        {
            // 六入
            return (integerPart + Math.Sign(value)) / factor;
        }
        else
        {
            // 五成双:检查前一位数字的奇偶性
            bool isEven = (integerPart % 2 == 0);
            return isEven ? integerPart / factor : (integerPart + Math.Sign(value)) / factor;
        }
    }
    
    /// <summary>
    /// 更精确的银行家舍入实现(处理边界情况)
    /// </summary>
    public static decimal PreciseBankersRound(decimal value, int decimals)
    {
        if (decimals < 0)
            throw new ArgumentException("小数位数不能为负数", nameof(decimals));
        
        decimal factor = (decimal)Math.Pow(10, decimals);
        decimal scaledValue = value * factor;
        
        decimal integerPart = Math.Truncate(scaledValue);
        decimal fractionalPart = scaledValue - integerPart;
        
        // 使用绝对值进行比较,避免符号问题
        decimal absFractional = Math.Abs(fractionalPart);
        
        if (absFractional < 0.5m)
        {
            return integerPart / factor;
        }
        else if (absFractional > 0.5m)
        {
            return (integerPart + Math.Sign(value)) / factor;
        }
        else
        {
            // 五成双规则
            bool isEven = (Math.Abs(integerPart) % 2 == 0);
            return isEven ? integerPart / factor : (integerPart + Math.Sign(value)) / factor;
        }
    }
}

方法三:自定义实现1

 

public decimal Banker(decimal value, int digital)
{
    string strVal;
    int pPot;
    decimal result;

    if (value < 0)
        result = Math.Abs(value);
    else
        result = value;

    strVal = result.ToString();
    pPot = strVal.IndexOf(".");

    if (pPot > 0 && pPot + digital + 1 < strVal.Length)
    {
        decimal ratio = (decimal)Math.Pow(10, digital);
        string usedVale = strVal.Substring(0, pPot + digital + 1);
        int nextNum = int.Parse(strVal.Substring(pPot + digital + 1, 1));
        decimal baseVal = decimal.Parse(usedVale);

        if (nextNum > 5)
        {
            result = baseVal + 1 / ratio;
        }
        else if (nextNum < 5)
        {
            result = baseVal;
        }
        else
        {
            decimal elapse = 0;
            if (pPot + digital + 2 < strVal.Length)
                elapse = Math.Abs(decimal.Parse(strVal) - baseVal);

            if (elapse > 0)
            {
                result = baseVal + 1 / ratio;
            }
            else
            {
                if (digital > 0 && int.Parse(strVal.Substring(pPot + digital, 1)) % 2 != 0)
                {
                    result = baseVal + 1 / ratio;
                }
                else if (digital == 0 && int.Parse(strVal.Substring(pPot - 1, 1)) % 2 != 0)
                {
                    result = baseVal + 1 / ratio;
                }
                else
                {
                    result = baseVal;
                }
            }
        }
    }
    if (value < 0)
        result = result * -1;

    return result;
}

//延长精度位数后的四舍五入+银行家舍入法
public decimal BankerEx(decimal value, int digital)
{
    decimal result;
    if (value < 0)
        result = Math.Abs(value);
    else
        result = value;

    var ratio = (decimal)Math.Pow(10, digital);
    var scaledValue = RoundOld(result * ratio, digital - 1);
    var baseVal = Math.Truncate(scaledValue);
    var elapse = Math.Abs(scaledValue - baseVal);

    if (elapse > (decimal)0.5)
        result = (baseVal + 1) / ratio;
    else if (elapse < (decimal)0.5)
        result = baseVal / ratio;
    else
        result = (baseVal % 2 == 0 ? baseVal : baseVal + 1) / ratio;

    if (value < 0)
        result = result * -1;

    return result;
}

//传统四舍五入法
public decimal RoundOld(decimal value, int digital)
{
    decimal result;
    if (value < 0)
        result = Math.Abs(value);
    else
        result = value;

    var ratio = (decimal)Math.Pow(10, digital);
    var scaledValue = result * ratio;
    var baseVal = Math.Truncate(scaledValue);
    var elapse = Math.Abs(scaledValue - baseVal);

    if (elapse < (decimal)0.5)
        result = baseVal / ratio;
    else
        result = (baseVal + 1) / ratio;

    if (value < 0)
        result = result * -1;

    return result;
}

 

posted on 2025-11-13 10:52  wangzhiliang  阅读(1)  评论(0)    收藏  举报

导航