質問

している場合は、ダブル(234.004223することができます。 たいと思い丸のこのx桁C#.

今までは、できる方法を探丸x桁が単に除去する精度があれば0に多くの注目を集めています。

例えば、0.086一位になり0.1,いつづけたいと思っていますので滞在の0.08.

役に立ちましたか?

解決

フレームワークは、有効桁数に丸める(またはあなたの例のように、切り捨て)する組み込みの機能はありません。あなたがこれを行うことができます一つの方法は、しかし、あなたの最初の有効桁が右に小数点以下になるように縮小、その後、円形(または切り捨て)、あなたの数を拡張することです。次のコードは、トリックを行う必要があります:

static double RoundToSignificantDigits(this double d, int digits){
    if(d == 0)
        return 0;

    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
    return scale * Math.Round(d / scale, digits);
}

あなたの例のように、あなたが本当に切り捨てるしたい、場合は、あなたが欲しいます:

static double TruncateToSignificantDigits(this double d, int digits){
    if(d == 0)
        return 0;

    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1 - digits);
    return scale * Math.Truncate(d / scale);
}

他のヒント

私は数ヶ月のためにpDaddyのsigfig機能を使用して、その中にバグを発見してきました。あなたは、負の数のログを取ることができないので、dが負の場合の結果はNaNである。

以下のバグを修正します:

public static double SetSigFigs(double d, int digits)
{   
    if(d == 0)
        return 0;

    decimal scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);

    return (double) (scale * Math.Round((decimal)d / scale, digits));
}
あなたがまったくのxの小数点以下の桁数に四捨五入したくないようにそれは私に聞こえる - あなたがX桁に丸めます。あなたの例ではだから、あなたは0.086に1桁ではなく、小数第1位を四捨五入したい。

さて、ダブル、有効桁数に丸めを使用して、で開始するダブルスが格納されている方法による問題があります。たとえば、あなたは0.1〜のの近いものに0.12を丸めることができますが、0.1は、二重のように正確に表現することができません。あなたが実際に小数を使用すべきではありませんか?また、これは表示目的のために、実際のですか?それは表示目的のためなら、私はあなたが実際に有効桁の関連番号を文字列に直接、二重に変換する必要がある疑います。

あなたはそれらの点に答えることができる場合、私はいくつかの適切なコードを思い付くしようとすることができます。それがうまく行くための最良の方法かもしれ(その後の適切な丸め行動をとる当時と)「完全な」数値を文字列に変換してから最初の重要な数字を見つけることによって、文字列として有効桁数に変換し、音としてひどいですます。

の場合は表示目的としている状態のコメントをJon Skeetの解答)を使用してみてくださいGn フォーマット指定子.場所 n の番号の桁-うりの楽しさがさらに広がります。

この例では利用したい場合は3桁印刷出力のコメントでそれぞれの線路):

    Console.WriteLine(1.2345e-10.ToString("G3"));//1.23E-10
    Console.WriteLine(1.2345e-5.ToString("G3")); //1.23E-05
    Console.WriteLine(1.2345e-4.ToString("G3")); //0.000123
    Console.WriteLine(1.2345e-3.ToString("G3")); //0.00123
    Console.WriteLine(1.2345e-2.ToString("G3")); //0.0123
    Console.WriteLine(1.2345e-1.ToString("G3")); //0.123
    Console.WriteLine(1.2345e2.ToString("G3"));  //123
    Console.WriteLine(1.2345e3.ToString("G3"));  //1.23E+03
    Console.WriteLine(1.2345e4.ToString("G3"));  //1.23E+04
    Console.WriteLine(1.2345e5.ToString("G3"));  //1.23E+05
    Console.WriteLine(1.2345e10.ToString("G3")); //1.23E+10

私はPパパとエリックの方法で2つのバグを発見しました。これは、例えばこのQ&AにアンドリューHancoxから提示された精度誤差を解決します。ラウンド方向での問題もありました。有効数字2桁と1050は1000.0ではない、それは1100.0です。丸めがMidpointRounding.AwayFromZeroで固定した。

static void Main(string[] args) {
  double x = RoundToSignificantDigits(1050, 2); // Old = 1000.0, New = 1100.0
  double y = RoundToSignificantDigits(5084611353.0, 4); // Old = 5084999999.999999, New = 5085000000.0
  double z = RoundToSignificantDigits(50.846, 4); // Old = 50.849999999999994, New =  50.85
}

static double RoundToSignificantDigits(double d, int digits) {
  if (d == 0.0) {
    return 0.0;
  }
  else {
    double leftSideNumbers = Math.Floor(Math.Log10(Math.Abs(d))) + 1;
    double scale = Math.Pow(10, leftSideNumbers);
    double result = scale * Math.Round(d / scale, digits, MidpointRounding.AwayFromZero);

    // Clean possible precision error.
    if ((int)leftSideNumbers >= digits) {
      return Math.Round(result, 0, MidpointRounding.AwayFromZero);
    }
    else {
      return Math.Round(result, digits - (int)leftSideNumbers, MidpointRounding.AwayFromZero);
    }
  }
}

ジョンスキートとしては言及:より良いテキストのドメインでこれを取り扱います。原則として:表示目的のために、/ラウンドあなたの浮動小数点値を変更しようとしない、それは決してかなり100%に動作します。ディスプレイは二の次であり、あなたは、文字列を扱うこれらのような特別なフォーマット要件を処理する必要があります。

I以下の私のソリューションは、数年前に実施され、非常に信頼性が実証されています。それは徹底的にテストされていて、それがまた非常によく実行されます。 Pパパ/エリックのソリューションよりも実行時間の約5倍の長ます。

コードで以下に与えられた入力+出力の

using System;
using System.Text;

namespace KZ.SigDig
{
    public static class SignificantDigits
    {
        public static string DecimalSeparator;

        static SignificantDigits()
        {
            System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
            DecimalSeparator = ci.NumberFormat.NumberDecimalSeparator;
        }

        /// <summary>
        /// Format a double to a given number of significant digits.
        /// </summary>
        /// <example>
        /// 0.086 -> "0.09" (digits = 1)
        /// 0.00030908 -> "0.00031" (digits = 2)
        /// 1239451.0 -> "1240000" (digits = 3)
        /// 5084611353.0 -> "5085000000" (digits = 4)
        /// 0.00000000000000000846113537656557 -> "0.00000000000000000846114" (digits = 6)
        /// 50.8437 -> "50.84" (digits = 4)
        /// 50.846 -> "50.85" (digits = 4)
        /// 990.0 -> "1000" (digits = 1)
        /// -5488.0 -> "-5000" (digits = 1)
        /// -990.0 -> "-1000" (digits = 1)
        /// 0.0000789 -> "0.000079" (digits = 2)
        /// </example>
        public static string Format(double number, int digits, bool showTrailingZeros = true, bool alwaysShowDecimalSeparator = false)
        {
            if (Double.IsNaN(number) ||
                Double.IsInfinity(number))
            {
                return number.ToString();
            }

            string sSign = "";
            string sBefore = "0"; // Before the decimal separator
            string sAfter = ""; // After the decimal separator

            if (number != 0d)
            {
                if (digits < 1)
                {
                    throw new ArgumentException("The digits parameter must be greater than zero.");
                }

                if (number < 0d)
                {
                    sSign = "-";
                    number = Math.Abs(number);
                }

                // Use scientific formatting as an intermediate step
                string sFormatString = "{0:" + new String('#', digits) + "E0}";
                string sScientific = String.Format(sFormatString, number);

                string sSignificand = sScientific.Substring(0, digits);
                int exponent = Int32.Parse(sScientific.Substring(digits + 1));
                // (the significand now already contains the requested number of digits with no decimal separator in it)

                StringBuilder sFractionalBreakup = new StringBuilder(sSignificand);

                if (!showTrailingZeros)
                {
                    while (sFractionalBreakup[sFractionalBreakup.Length - 1] == '0')
                    {
                        sFractionalBreakup.Length--;
                        exponent++;
                    }
                }

                // Place decimal separator (insert zeros if necessary)

                int separatorPosition = 0;

                if ((sFractionalBreakup.Length + exponent) < 1)
                {
                    sFractionalBreakup.Insert(0, "0", 1 - sFractionalBreakup.Length - exponent);
                    separatorPosition = 1;
                }
                else if (exponent > 0)
                {
                    sFractionalBreakup.Append('0', exponent);
                    separatorPosition = sFractionalBreakup.Length;
                }
                else
                {
                    separatorPosition = sFractionalBreakup.Length + exponent;
                }

                sBefore = sFractionalBreakup.ToString();

                if (separatorPosition < sBefore.Length)
                {
                    sAfter = sBefore.Substring(separatorPosition);
                    sBefore = sBefore.Remove(separatorPosition);
                }
            }

            string sReturnValue = sSign + sBefore;

            if (sAfter == "")
            {
                if (alwaysShowDecimalSeparator)
                {
                    sReturnValue += DecimalSeparator + "0";
                }
            }
            else
            {
                sReturnValue += DecimalSeparator + sAfter;
            }

            return sReturnValue;
        }
    }
}

double の Math.Round() には欠陥があります (「呼び出し者への注意事項」を参照) ドキュメンテーション)。四捨五入された数値に 10 進指数を乗算する後のステップでは、後続の桁に浮動小数点エラーがさらに発生します。@Rowanto のように別の Round() を使用しても、確実には役に立たず、他の問題が発生します。ただし、10 進数を使用する場合は、10 のべき乗による乗算と除算と同様に、Math.Round() が信頼できます。

static ClassName()
{
    powersOf10 = new decimal[28 + 1 + 28];
    powersOf10[28] = 1;
    decimal pup = 1, pdown = 1;
    for (int i = 1; i < 29; i++) {
        pup *= 10;
        powersOf10[i + 28] = pup;
        pdown /= 10;
        powersOf10[28 - i] = pdown;
    }
}

/// <summary>Powers of 10 indexed by power+28.  These are all the powers
/// of 10 that can be represented using decimal.</summary>
static decimal[] powersOf10;

static double RoundToSignificantDigits(double v, int digits)
{
    if (v == 0.0 || Double.IsNaN(v) || Double.IsInfinity(v)) {
        return v;
    } else {
        int decimal_exponent = (int)Math.Floor(Math.Log10(Math.Abs(v))) + 1;
        if (decimal_exponent < -28 + digits || decimal_exponent > 28 - digits) {
            // Decimals won't help outside their range of representation.
            // Insert flawed Double solutions here if you like.
            return v;
        } else {
            decimal d = (decimal)v;
            decimal scale = powersOf10[decimal_exponent + 28];
            return (double)(scale * Math.Round(d / scale, digits, MidpointRounding.AwayFromZero));
        }
    }
}

inputNumberが、その後significantDigitsRequiredは、以下の擬似コードへの答えで、小数点以下significantDigitsResultに変換する必要が入力することがしてみましょう。

integerPortion = Math.truncate(**inputNumber**)

decimalPortion = myNumber-IntegerPortion

if( decimalPortion <> 0 )
{

 significantDigitsStartFrom = Math.Ceil(-log10(decimalPortion))

 scaleRequiredForTruncation= Math.Pow(10,significantDigitsStartFrom-1+**significantDigitsRequired**)

**siginficantDigitsResult** = integerPortion + ( Math.Truncate (decimalPortion*scaleRequiredForTruncation))/scaleRequiredForTruncation

}
else
{

  **siginficantDigitsResult** = integerPortion

}

の精神に同意します ジョンの評価:

ひどいように聞こえるかもしれませんが、数値を「完全な」文字列に変換し、最初の有効桁を見つけて (その後、適切な丸め処理を実行して) 有効桁数を文字列として変換するのが最善の方法かもしれません。 。

有効数字の四捨五入が必要でした 近似 そして パフォーマンスクリティカルではない 計算目的では、「G」形式による形式解析のラウンドトリップで十分です。

public static double RoundToSignificantDigits(this double value, int numberOfSignificantDigits)
{
    return double.Parse(value.ToString("G" + numberOfSignificantDigits));
}

私はちょうどでします:

int integer1 = Math.Round(double you want to round, 
    significant figures you want to round to)

ここでは、私はC ++

で行ったものです
/*
    I had this same problem I was writing a design sheet and
    the standard values were rounded. So not to give my
    values an advantage in a later comparison I need the
    number rounded, so I wrote this bit of code.

    It will round any double to a given number of significant
    figures. But I have a limited range written into the
    subroutine. This is to save time as my numbers were not
    very large or very small. But you can easily change that
    to the full double range, but it will take more time.

    Ross Mckinstray
    rmckinstray01@gmail.com
*/

#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#include <cmath>
#include <iomanip>

#using namespace std;

double round_off(double input, int places) {
    double roundA;
    double range = pow(10, 10); // This limits the range of the rounder to 10/10^10 - 10*10^10 if you want more change range;
    for (double j = 10/range; j< 10*range;) {
        if (input >= j && input < j*10){
            double figures = pow(10, places)/10;
            roundA = roundf(input/(j/figures))*(j/figures);
        }
        j = j*10;
    }
    cout << "\n in sub after loop";
    if (input <= 10/(10*10) && input >= 10*10) {
        roundA = input;
        cout << "\nDID NOT ROUND change range";
    }
    return roundA;
}

int main() {
    double number, sig_fig;

    do {
        cout << "\nEnter number ";
        cin >> number;
        cout << "\nEnter sig_fig ";
        cin >> sig_fig;
        double output = round_off(number, sig_fig);

        cout << setprecision(10);
        cout << "\n I= " << number;
        cout << "\n r= " <<output;
        cout << "\nEnter 0 as number to exit loop";
    }
    while (number != 0);

    return 0;
}

うまくいけば、私はそれをフォーマット何も変更しませんでした。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top