سؤال

إذا كان لدي رقم مزدوج (234.004223)، وما إلى ذلك، أود تقريب هذا إلى x أرقام مهمة في C#.

حتى الآن لا يمكنني العثور إلا على طرق للتقريب إلى x من المنازل العشرية، ولكن هذا ببساطة يزيل الدقة إذا كان هناك أي أصفار في الرقم.

على سبيل المثال، 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);
}

نصائح أخرى

ولقد تم استخدام وظيفة sigfig pDaddy لبضعة أشهر وجدت خطأ في ذلك. لا يمكنك التقاط سجل عدد سلبية، لذلك إذا د سلبي على النتائج هو نان.

وفيما يلي بتصحيح الخطأ:

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 الأرقام المعنوية. حتى في المثال الخاص بك، وتريد أن الجولة رقم كبير 0.086 إلى واحد، وليس مكان واحد عشري.

والآن، وذلك باستخدام مزدوج والتقريب إلى عدد من الأرقام المعنوية إشكالية في البداية، بسبب الطريقة التي يتم تخزينها الزوجي. على سبيل المثال، هل يمكن أن جولة 0.12 إلى شيء <م> وثيقة إلى 0.1، ولكن 0.1 ليست للتمثيل تماما كما مزدوج. هل أنت متأكد أنك لا ينبغي أن يكون في الواقع باستخدام عشري؟ بدلا من ذلك، وهذا هو في الواقع لأغراض العرض؟ لو كان لأغراض العرض، وأظن يجب تحويل الواقع المزدوج مباشرة إلى سلسلة مع عدد هام من الأرقام المعنوية.

إذا كنت تستطيع الإجابة على هذه النقاط، ويمكنني أن محاولة الخروج ببعض الرمز المناسب. مروعا كما يبدو، وتحويل إلى عدد من الأرقام المعنوية كسلسلة من خلال تحويل العدد إلى سلسلة "الكامل"، ومن ثم العثور على رقم كبير الأول (ومن ثم اتخاذ الإجراءات التقريب المناسب بعد ذلك) قد يكون أفضل طريقة للذهاب .

إذا كان لأغراض العرض (كما تصرح في تعليق على الجواب جون السكيت)، ويجب عليك استخدام تك <لأ href = "http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx #GFormatString "يختلط =" noreferrer "> تنسيق محدد . حيث <م> ن هو عدد الأرقام المعنوية - بالضبط ما كنت بعد.

وهنا هو مثال الاستخدام إذا كنت تريد 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 الأب واريك. هذا يحل على سبيل المثال الخطأ الدقة التي قدمها أندرو هانكوكس في هذا Q & A. وكان هناك أيضا مشكلة مع الاتجاهات مستديرة. 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 تنفيذها منذ عدة سنوات وأثبتت موثوق بها للغاية. وقد تم اختباره جيدا وينفذ بشكل جيد أيضا. حوالي 5 مرات أطول في وقت التنفيذ من حل P الأب / اريك.

والأمثلة على مدخلات الانتاج + الواردة أدناه في التعليمات البرمجية.

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;
        }
    }
}

وMath.Round () على الزوجي معيب (انظر ملاحظات على المتصلون في وثائق ). في خطوة لاحقة من ضرب عدد مقربة احتياطية من الأس العشري لها سوف أعرض مزيد العائمة أخطاء نقطة في خانة زائدة. باستخدام جولة أخرى () كما يفعلRowanto لن يساعد بشكل موثوق ويعاني من مشاكل أخرى. ولكن إذا كنت على استعداد للذهاب عبر عشري ثم Math.Round () غير موثوق بها، كما هو التكاثر والانقسام من قبل قوى 10:

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));
        }
    }
}

وهذا السؤال هو مشابهة إلى واحد كنت طالبا:

أرقام

تنسيق مع شخصيات كبيرة في C #

وهكذا يمكنك أن تفعل ما يلي:

double Input2 = 234.004223;
string Result2 = Math.Floor(Input2) + Convert.ToDouble(String.Format("{0:G1}", Input2 - Math.Floor(Input2))).ToString("R6");

وتقريب إلى 1 رقم كبير.

واسمحوا 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