سؤال

كيف يمكنك أن تدور أي الرقم (وليس فقط الأعداد الصحيحة> 0) إلى n أرقام مهمة؟

على سبيل المثال ، إذا كنت أرغب في التقريب إلى ثلاثة أرقام مهمة ، فأنا أبحث عن صيغة قد تستغرق:

1،239،451 وإعادة 1،240،000

12.1257 والعودة 12.1

.0681 والعودة .0681

5 والعودة 5

بطبيعة الحال ، لا ينبغي أن تكون الخوارزمية متشددة للتعامل مع N من 3 فقط ، على الرغم من أن ذلك سيكون بداية.

هل كانت مفيدة؟

المحلول

إليك نفس الرمز في Java بدون 12.100000000000001 Bug إجابات أخرى

كما قمت بإزالة الكود المتكرر ، تغيرت power إلى عدد صحيح من النوع لمنع المشكلات العائمة عندما n - d يتم ، وجعل الوسيط الطويل أكثر وضوحا

كان سبب الخلل بسبب ضرب عدد كبير مع عدد صغير. بدلاً من ذلك ، أقسم رقمين من الحجم المماثل.

تعديل
تم إصلاح المزيد من الأخطاء. تمت إضافة التحقق من 0 لأنه سيؤدي إلى NAN. جعلت الوظيفة تعمل فعليًا مع الأرقام السلبية (لا يتعامل الكود الأصلي مع الأرقام السالبة لأن سجل الرقم السالب هو رقم معقد)

public static double roundToSignificantFigures(double num, int n) {
    if(num == 0) {
        return 0;
    }

    final double d = Math.ceil(Math.log10(num < 0 ? -num: num));
    final int power = n - (int) d;

    final double magnitude = Math.pow(10, power);
    final long shifted = Math.round(num*magnitude);
    return shifted/magnitude;
}

نصائح أخرى

إليك تطبيق JavaScript قصيرًا وحلوًا:

function sigFigs(n, sig) {
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
}

alert(sigFigs(1234567, 3)); // Gives 1230000
alert(sigFigs(0.06805, 3)); // Gives 0.0681
alert(sigFigs(5, 3)); // Gives 5

ملخص:

double roundit(double num, double N)
{
    double d = log10(num);
    double power;
    if (num > 0)
    {
        d = ceil(d);
        power = -(d-N);
    }
    else
    {
        d = floor(d); 
        power = -(d-N);
    }

    return (int)(num * pow(10.0, power) + 0.5) * pow(10.0, -power);
}

لذلك تحتاج إلى العثور على المكان العشري لأول رقم غير صفري ، ثم احفظ أرقام N-1 التالية ، ثم حول الرقم التاسع بناءً على الباقي.

يمكننا استخدام السجل للقيام الأول.

log 1239451 = 6.09
log 12.1257 = 1.08
log 0.0681  = -1.16

لذلك بالنسبة للأرقام> 0 ، خذ سقف السجل. للأرقام <0 ، خذ أرضية السجل.

الآن لدينا الرقم d: 7 في الحالة الأولى ، 2 في الثاني ، -2 في الثالث.

علينا أن ندير (d-N)الرقم. شيء مثل:

double roundedrest = num * pow(10, -(d-N));

pow(1239451, -4) = 123.9451
pow(12.1257, 1)  = 121.257
pow(0.0681, 4)   = 681

ثم افعل الشيء التقريب القياسي:

roundedrest = (int)(roundedrest + 0.5);

والتراجع عن الأسير.

roundednum = pow(roundedrest, -(power))

حيث الطاقة هي القوة المحسوبة أعلاه.


حول الدقة: إجابة Pyristical هي في الواقع أقرب إلى النتيجة الحقيقية. ولكن لاحظ أنه لا يمكنك تمثيل 12.1 في أي حال. إذا قمت بطباعة الإجابات على النحو التالي:

System.out.println(new BigDecimal(n));

الإجابات هي:

Pyro's: 12.0999999999999996447286321199499070644378662109375
Mine: 12.10000000000000142108547152020037174224853515625
Printing 12.1 directly: 12.0999999999999996447286321199499070644378662109375

لذلك ، استخدم إجابة Pyro!

ليس تطبيق JavaScript "القصير والحلو"

Number(n).toPrecision(sig)

على سبيل المثال

alert(Number(12345).toPrecision(3)

?

عذرًا ، أنا لست منسوبًا هنا ، إنه مجرد استخدام وظيفة "Roundit" من Claudiu و .toprecision في JavaScript يعطيني نتائج مختلفة ولكن فقط في تقريب الرقم الأخير.

JavaScript:

Number(8.14301).toPrecision(4) == 8.143

.صافي

roundit(8.14301,4) == 8.144

لا يزال حل Pyrolistical (لطيف للغاية!) مشكلة. الحد الأقصى لقيمة مزدوجة في Java هو في ترتيب 10^308 ، في حين أن الحد الأدنى للقيمة في ترتيب 10^-324. لذلك ، يمكنك مواجهة المتاعب عند تطبيق الوظيفة roundToSignificantFigures لشيء ما هو ضمن بضع قوى من عشرة من Double.MIN_VALUE. على سبيل المثال ، عند الاتصال

roundToSignificantFigures(1.234E-310, 3);

ثم المتغير power سيكون لها القيمة 3 - (-309) = 312. وبالتالي ، المتغير magnitude سيصبح Infinity, ، وكل ذلك القمامة منذ ذلك الحين. لحسن الحظ ، هذه ليست مشكلة لا يمكن التغلب عليها: إنها فقط عامل magnitude هذا يفيض. ما يهم حقًا هو منتج num * magnitude, ، وهذا لا يفيض. إحدى طرق حل هذا عن طريق تفكيك الضرب حسب العامل magintude في خطوتين:


 public static double roundToNumberOfSignificantDigits(double num, int n) {

    final double maxPowerOfTen = Math.floor(Math.log10(Double.MAX_VALUE));

    if(num == 0) {
        return 0;
    }

    final double d = Math.ceil(Math.log10(num < 0 ? -num: num));
    final int power = n - (int) d;

    double firstMagnitudeFactor = 1.0;
    double secondMagnitudeFactor = 1.0;
    if (power > maxPowerOfTen) {
        firstMagnitudeFactor = Math.pow(10.0, maxPowerOfTen);
        secondMagnitudeFactor = Math.pow(10.0, (double) power - maxPowerOfTen);
    } else {
        firstMagnitudeFactor = Math.pow(10.0, (double) power);
    }

    double toBeRounded = num * firstMagnitudeFactor;
    toBeRounded *= secondMagnitudeFactor;

    final long shifted = Math.round(toBeRounded);
    double rounded = ((double) shifted) / firstMagnitudeFactor;
    rounded /= secondMagnitudeFactor;
    return rounded;
}

ماذا عن حل جافا هذا:

double roundToSignificantFigure(double num, int precision){
 return new BigDecimal(num)
            .round(new MathContext(precision, RoundingMode.HALF_EVEN))
            .doubleValue(); 
}

فيما يلي نسخة معدلة من JavaScript Ates التي تتعامل مع الأرقام السلبية.

function sigFigs(n, sig) {
    if ( n === 0 )
        return 0
    var mult = Math.pow(10,
        sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
 }

جاء هذا متأخرا 5 سنوات ، ولكن على الرغم من أنني سأشارك للآخرين ما زالوا يعانون من نفس القضية. يعجبني لأنه بسيط ولا توجد حسابات على جانب الكود. نرى طرق مدمجة لعرض أرقام مهمة لمزيد من المعلومات.

هذا إذا كنت تريد طباعته فقط.

public String toSignificantFiguresString(BigDecimal bd, int significantFigures){
    return String.format("%."+significantFigures+"G", bd);
}

هذا إذا كنت تريد تحويله:

public BigDecimal toSignificantFigures(BigDecimal bd, int significantFigures){
    String s = String.format("%."+significantFigures+"G", bd);
    BigDecimal result = new BigDecimal(s);
    return result;
}

إليك مثال على ذلك:

BigDecimal bd = toSignificantFigures(BigDecimal.valueOf(0.0681), 2);

هل حاولت فقط ترميزها بالطريقة التي ستفعل بها باليد؟

  1. تحويل الرقم إلى سلسلة
  2. بدءًا من بداية السلسلة ، عدد الأرقام - الأصفار الرائدة ليست مهمة ، كل شيء آخر.
  3. عندما تصل إلى الرقم "التاسع" ، قم بإلقاء نظرة على الرقم التالي وإذا كان 5 أو أعلى ، فقم بدوره.
  4. استبدل جميع الأرقام الزائدة بالأصفار.

تصحيح ، 2009-10-26

في الأساس ، بالنسبة إلى N كبير كسور الأرقام:

• اضرب الرقم بمقدار 10ن
• أضف 0.5
• اقتطاع أرقام الكسر (أي ، اقتطاع النتيجة في عدد صحيح)
• قسمة على 10ن

ل N كبير متكامل (غير مسجلة) الأرقام:

• قسّم الرقم على 10ن
• أضف 0.5
• اقتطاع أرقام الكسر (أي ، اقتطاع النتيجة في عدد صحيح)
• اضرب ب 10ن

يمكنك القيام بذلك على أي آلة حاسبة ، على سبيل المثال ، يحتوي على مشغل "int" (اقتطاع عدد صحيح).

/**
 * Set Significant Digits.
 * @param value value
 * @param digits digits
 * @return
 */
public static BigDecimal setSignificantDigits(BigDecimal value, int digits) {
    //# Start with the leftmost non-zero digit (e.g. the "1" in 1200, or the "2" in 0.0256).
    //# Keep n digits. Replace the rest with zeros.
    //# Round up by one if appropriate.
    int p = value.precision();
    int s = value.scale();
    if (p < digits) {
        value = value.setScale(s + digits - p); //, RoundingMode.HALF_UP
    }
    value = value.movePointRight(s).movePointLeft(p - digits).setScale(0, RoundingMode.HALF_UP)
        .movePointRight(p - digits).movePointLeft(s);
    s = (s > (p - digits)) ? (s - (p - digits)) : 0;
    return value.setScale(s);
}

فيما يلي رمز Pyrolistical (أعلى الإجابة حاليًا) في Visual Basic.net ، إذا احتاج أي شخص إلى:

Public Shared Function roundToSignificantDigits(ByVal num As Double, ByVal n As Integer) As Double
    If (num = 0) Then
        Return 0
    End If

    Dim d As Double = Math.Ceiling(Math.Log10(If(num < 0, -num, num)))
    Dim power As Integer = n - CInt(d)
    Dim magnitude As Double = Math.Pow(10, power)
    Dim shifted As Double = Math.Round(num * magnitude)
    Return shifted / magnitude
End Function

JavaScript:

Number( my_number.toPrecision(3) );

ال Number ستغير الوظيفة إخراج النموذج "8.143e+5" إلى "814300".

هذا هو الذي توصلت إليه في VB:

Function SF(n As Double, SigFigs As Integer)
    Dim l As Integer = n.ToString.Length
    n = n / 10 ^ (l - SigFigs)
    n = Math.Round(n)
    n = n * 10 ^ (l - SigFigs)
    Return n
End Function

return new BigDecimal(value, new MathContext(significantFigures, RoundingMode.HALF_UP)).doubleValue();

كنت بحاجة إلى ذلك في GO ، والذي كان معقدًا بعض الشيء بسبب عدم وجود مكتبة Go Standard math.Round() (قبل GO1.10). لذلك اضطررت إلى سوط ذلك أيضًا. ها هي ترجمتي إجابة بيروليستوسيت ممتازة:

// TODO: replace in go1.10 with math.Round()
func round(x float64) float64 {
    return float64(int64(x + 0.5))
}

// SignificantDigits rounds a float64 to digits significant digits.
// Translated from Java at https://stackoverflow.com/a/1581007/1068283
func SignificantDigits(x float64, digits int) float64 {
    if x == 0 {
        return 0
    }

    power := digits - int(math.Ceil(math.Log10(math.Abs(x))))
    magnitude := math.Pow(10, float64(power))
    shifted := round(x * magnitude)
    return shifted / magnitude
}
public static double roundToSignificantDigits(double num, int n) {
    return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}

يستخدم هذا الرمز وظيفة التنسيق في ثنائية المليئة والتي تحولت إلى وظيفة التقريب

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top