2013年12月5日 星期四

關於四捨五入的各種方法

如果我們要對整數 四捨五入可以使用
Math.Ceiling 或 Math.floor
如果數字7.5 Ceiling 後的結果是8
如果是 floor後的結果會是7

來看範例吧!

           double x = 5.5; //假如輸入5.5
            int s = 0; //宣告s 為輸出結果
            //下面使用Ceiling後 轉型為int
            s = Convert.ToInt16(Math.Ceiling(x));
            MessageBox.Show(s.ToString());
            s = Convert.ToInt16(Math.Floor(x));
            MessageBox.Show(s.ToString());
           
            //out 6
            //out 5


要注意的是
這個方法的行為會遵循 IEEE 標準 754,第 4 條。這種捨入有時稱為向正無限大方向捨入。換句話說,如果 a 是正值,則出現任何小數部分都會使 a 捨入至下一個最大整數。如果 a 是負值,則四捨五入運算會捨棄 a 的任何小數部分。這個方法的運算與 Floor(Double) 方法不同,其支援四捨五入成接近無限大的負數。

請看MSDN 的範例中的輸出
處理前 -0.12 
Cleiling後 = 0 
Floor = -1
注意的是-0.12是負號 Ceiling後結果並不會是 1
MSDN範例如下


double[] values = {7.03, 7.64, 0.12, -0.12, -7.1, -7.6};
Console.WriteLine("  Value          Ceiling          Floor\n");
foreach (double value in values)
   Console.WriteLine("{0,7} {1,16} {2,14}",
                     value, Math.Ceiling(value), Math.Floor(value));
// The example displays the following output to the console:
//         Value          Ceiling          Floor
//      
//          7.03                8              7
//          7.64                8              7
//          0.12                1              0
//         -0.12                0             -1
//          -7.1               -7             -8
//          -7.6               -7             -8


以上是單純的 進位

如果要處裡的數字是Double呢??
可以使用Round
將十進位的值捨入至最接近的整數值。
來看一下範例

            double[] values = { 2.125, 2.135, 2.145, 3.125, 3.135, 3.145 };
            foreach (double value in values)
                MessageBox.Show("原本是"+value.ToString()+"結果為"+Math.Round(value, 2));
            //  OutPut
            //  2.12
            //  2.13
            //  2.14
            //  3.12
            //  3.14
            //  3.14


Round 只說將十進位的值捨入至最接近的整數值。
並沒有說是 四捨五入 顯然這數值失真了

原因如下
由於將十進位值表示為浮點數,或者對浮點數值執行算術運算可能導致精確度喪失,因此在某些情況下,Round(Double) 方法可能無法將中點值四捨五入到最接近的偶數整數。


所以我們必須使用 Decimal整數
但是使用了Decimal 並不會是我們要得四捨五入
假如1.5 並不會變成2  而是要1.6才會 變成2
下列範例 將使用
Math.Round (double, MidpointRounding) 來證明!


using System;
public class Example
{
   public static void Main()
   {
      Console.WriteLine("{0,-10} {1,-10} {2,-10} {3,-15}", "Value", "Default",
                        "ToEven", "AwayFromZero");
      for (decimal value = 12.0m; value <= 13.0m; value += 0.1m)
         Console.WriteLine("{0,-10} {1,-10} {2,-10} {3,-15}",
                           value, Math.Round(value),
                           Math.Round(value, MidpointRounding.ToEven),
                           Math.Round(value, MidpointRounding.AwayFromZero));
   }
}
// The example displays the following output:
//       Value      Default    ToEven     AwayFromZero
//       12           12             12             12
//       12.1        12             12             12
//       12.2        12             12             12
//       12.3        12             12             12
//       12.4        12             12             12
//       12.5        12             12             13
//       12.6        13             13             13
//       12.7        13             13             13
//       12.8        13             13             13
//       12.9        13             13             13
//       13.0        13             13             13


這方法使用十進位 decimal
由for迴圈 12.0 累加到13.0 每次+0.1
並且 輸出 三種數值
Math.Round(value)
Math.Round(value, MidpointRounding.ToEven)
Math.Round(value, MidpointRounding.AwayFromZero))

讓讀者能更清楚知道 這之間的差異
Math.Round(Decimal)光是使用是不夠的
我們必須搭配 MidpointRounding.AwayFromZero 這個MODE

所以如果我們要完美得呈現四捨五入
我們必須使用這方法
Math.Round 方法 (Decimal, Int32, MidpointRounding)

下面將示範此方法

            Decimal[] values1 = { 2.125m, 2.135m, 2.145m, 3.125m, 3.135m, 3.145m };
            foreach (Decimal OK in values1)
            {
                MessageBox.Show("原本是" + OK.ToString() + "  結果為" +
                    Math.Round(OK, 2, MidpointRounding.AwayFromZero));
            }
            // OUTPUT
            // 原本是2.125 結果為2.13
            // 原本是2.135 結果為2.14
            // 原本是2.145 結果為2.15
            // 原本是3.125 結果為3.13
            // 原本是3.135 結果為3.14
            // 原本是3.145 結果為3.15




還有另外一種方法於書中範例中提到
當3.44444 + 0.00001
如何取小數點n位
呈現
3.5 
3.45
3.445
3.4445
下列範例為書中範例
可以解決這問題



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Round
{
    public partial class Frm_Main : Form
    {
        public Frm_Main()
        {
            InitializeComponent();
        }

        private void btn_Get_Click(object sender, EventArgs e)
        {
            double P_dbl_d1, P_dbl_d2;//定義兩個double類型變數
            if (double.TryParse(txt_add1.Text, out P_dbl_d1) &&//判斷輸入訊息是否正確
                double.TryParse(txt_add2.Text, out P_dbl_d2))
            {
                txt_add3.Text = //得到四捨五入後的值,傳值呼叫
                    Round(P_dbl_d1 + P_dbl_d2, cbox_select.SelectedIndex + 1).ToString();
            }
            else
            {
                MessageBox.Show(//提示錯誤訊息
                    "輸入數值不正確!", "提示!");
            }
        }

        /// <summary>
        /// 計算double值四捨五入的方法
        /// </summary>
        /// <param name="dbl">進行四捨五入的數值</param>
        /// <param name="i">保留的小數位</param>
        /// <returns>返回四捨五入後的double值</returns>
        internal double Round(double dbl, int i)
        {
            string P_str_dbl = dbl.ToString();//將double數值轉換為字串
            //將double數值小數位轉換為字串
            //注意! Split 傳回的是一個陣列
            //"."是分割字源[0]是小數點前[1]是小數點後
            string P_str_lower = P_str_dbl.Split('.')[1];
            int P_str_length = P_str_lower.Length;//計算double數值小數位長度
            dbl += GetValue(i, P_str_length);//開始進行四捨五入運算,傳直呼叫
            P_str_dbl = dbl.ToString();//將運算後的值轉換為字串
            if (P_str_dbl.Contains("."))//判斷是否存在小數位
            {
                string P_str_upper = //得到整數位字串
                    P_str_dbl.Split('.')[0];
                P_str_lower = P_str_dbl.Split('.')[1];//得到小數位字串
                if (P_str_lower.Length > i)//判斷數值位數是否大於保留小數位數
                {
                    P_str_lower = P_str_lower.Substring(//截取保留的小數位
                        0, i);//0起始位置為第一個小數,2是 娶幾個
                    return double.Parse(//返回double數值
                        P_str_upper + "." + P_str_lower);
                }
                else
                {
                    return double.Parse(P_str_dbl);//如數值位數小於保留小數位數則直接返回
                }
            }
            else
            {
                return double.Parse(P_str_dbl);//如果沒有小數位則直接返回值
            }
        }

        /// <summary>
        /// 得到小數數值的方法
        /// </summary>
        /// <param name="int_null">四捨五入保留的位數</param>
        /// <param name="length">四捨五入丟失的位數</param>
        /// <returns>返回小數值用於四捨五入計算</returns>
        internal double GetValue(int int_null, int length)
        {
            string P_str_dbl = "0.";//定義字串變數並賦值
            for (int i = 0; i < length; i++)//使用for循環新增小數位
            {
                if (i > int_null - 1)
                {
                    P_str_dbl += "5";//向小數的四捨五入部分加5
                }
                else
                {
                    P_str_dbl += "0";//向小數的保留部分加0
                }
            }
            return double.Parse(P_str_dbl);//返回小數數值 ,轉型為double
        }

        private void Frm_Main_Load(object sender, EventArgs e)
        {
            cbox_select.SelectedIndex = 0;//cbox_select控制元件默認選擇第一項
        }

    }
}









沒有留言:

張貼留言