سؤال

أريد أن أمثل رقم النقطة العائمة كسلسلة مدورة إلى عدد من الأرقام المهمة ، وعدم استخدام التنسيق الأسي. في الأساس ، أريد أن أعرض أي رقم فاصلة وتأكد من أنه "يبدو لطيفًا".

هناك عدة أجزاء لهذه المشكلة:

  • أحتاج إلى أن أكون قادرًا على تحديد عدد الأرقام المهمة.
  • يجب أن يكون عدد الأرقام المهمة متغيرة ، والتي لا يمكن القيام بها مع مشغل تنسيق السلسلة. [تحرير] لقد تم تصحيحها ؛ يمكن لمشغل تنسيق السلسلة القيام بذلك.
  • أحتاج إلى أن يتم تقريبها بالطريقة التي يتوقعها الشخص ، وليس شيئًا مثل 1.99999999999

لقد اكتشفت طريقة واحدة للقيام بذلك ، على الرغم من أنه يبدو وكأنه جولة عمل وليست مثالية تمامًا. (الحد الأقصى للدقة هو 15 رقمًا مهمًا.)

>>> def f(number, sigfig):
    return ("%.15f" % (round(number, int(-1 * floor(log10(number)) + (sigfig - 1))))).rstrip("0").rstrip(".")

>>> print f(0.1, 1)
0.1
>>> print f(0.0000000000368568, 2)
0.000000000037
>>> print f(756867, 3)
757000

هل هناك طريقة أفضل للقيام بذلك؟ لماذا لا يكون للبيثون وظيفة مدمجة لهذا؟

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

المحلول

يبدو أنه لا توجد خدعة تنسيق سلسلة مدمجة تتيح لك (1) طباعة عوامات تظهر أول رقم مهم بعد المكان العشري الخامس عشر و (2) ليس بالترميز العلمي. بحيث يترك معالجة السلسلة اليدوية.

أدناه أنا استخدم decimal وحدة لاستخراج الأرقام العشرية من العائمة. ال float_to_decimal يتم استخدام الوظيفة لتحويل الطفو إلى أ Decimal هدف. الطريقة الواضحة decimal.Decimal(str(f)) خطأ بسبب str(f) يمكن أن تفقد أرقام كبيرة.

float_to_decimal تم رفعه من وثائق الوحدة العشرية.

بمجرد الحصول على الأرقام العشرية على أنها مجموعة من INTs ، فإن الكود أدناه يقوم بالشيء الواضح: قم بقطع العدد المطلوب من الأرقام الرائعة ، وجولة إذا لزم الأمر ، انضم إلى الأرقام معًا في سلسلة ، وصياغة على علامة ، وضع علامة عشرية نقطة وأصفار إلى اليسار أو اليمين حسب الاقتضاء.

في الأسفل ، ستجد بعض الحالات التي اعتدت على اختبارها f وظيفة.

import decimal

def float_to_decimal(f):
    # http://docs.python.org/library/decimal.html#decimal-faq
    "Convert a floating point number to a Decimal with no loss of information"
    n, d = f.as_integer_ratio()
    numerator, denominator = decimal.Decimal(n), decimal.Decimal(d)
    ctx = decimal.Context(prec=60)
    result = ctx.divide(numerator, denominator)
    while ctx.flags[decimal.Inexact]:
        ctx.flags[decimal.Inexact] = False
        ctx.prec *= 2
        result = ctx.divide(numerator, denominator)
    return result 

def f(number, sigfig):
    # http://stackoverflow.com/questions/2663612/nicely-representing-a-floating-point-number-in-python/2663623#2663623
    assert(sigfig>0)
    try:
        d=decimal.Decimal(number)
    except TypeError:
        d=float_to_decimal(float(number))
    sign,digits,exponent=d.as_tuple()
    if len(digits) < sigfig:
        digits = list(digits)
        digits.extend([0] * (sigfig - len(digits)))    
    shift=d.adjusted()
    result=int(''.join(map(str,digits[:sigfig])))
    # Round the result
    if len(digits)>sigfig and digits[sigfig]>=5: result+=1
    result=list(str(result))
    # Rounding can change the length of result
    # If so, adjust shift
    shift+=len(result)-sigfig
    # reset len of result to sigfig
    result=result[:sigfig]
    if shift >= sigfig-1:
        # Tack more zeros on the end
        result+=['0']*(shift-sigfig+1)
    elif 0<=shift:
        # Place the decimal point in between digits
        result.insert(shift+1,'.')
    else:
        # Tack zeros on the front
        assert(shift<0)
        result=['0.']+['0']*(-shift-1)+result
    if sign:
        result.insert(0,'-')
    return ''.join(result)

if __name__=='__main__':
    tests=[
        (0.1, 1, '0.1'),
        (0.0000000000368568, 2,'0.000000000037'),           
        (0.00000000000000000000368568, 2,'0.0000000000000000000037'),
        (756867, 3, '757000'),
        (-756867, 3, '-757000'),
        (-756867, 1, '-800000'),
        (0.0999999999999,1,'0.1'),
        (0.00999999999999,1,'0.01'),
        (0.00999999999999,2,'0.010'),
        (0.0099,2,'0.0099'),         
        (1.999999999999,1,'2'),
        (1.999999999999,2,'2.0'),           
        (34500000000000000000000, 17, '34500000000000000000000'),
        ('34500000000000000000000', 17, '34500000000000000000000'),  
        (756867, 7, '756867.0'),
        ]

    for number,sigfig,answer in tests:
        try:
            result=f(number,sigfig)
            assert(result==answer)
            print(result)
        except AssertionError:
            print('Error',number,sigfig,result,answer)

نصائح أخرى

إذا كنت تريد دقة النقطة العائمة ، فأنت بحاجة إلى استخدام decimal الوحدة النمطية ، التي هي جزء من مكتبة بيثون القياسية:

>>> import decimal
>>> d = decimal.Decimal('0.0000000000368568')
>>> print '%.15f' % d
0.000000000036857

فيما يلي مقتطف يؤسس قيمة وفقًا لأشرطة الخطأ المحددة.

from math import floor, log10, round

def sigfig3(v, errplus, errmin):
    i = int(floor(-log10(max(errplus,errmin)) + 2))
    if i > 0:
        fmt = "%%.%df" % (i)
        return "{%s}^{%s}_{%s}" % (fmt % v,fmt % errplus, fmt % errmin)
    else:
        return "{%d}^{%d}_{%d}" % (round(v, i),round(errplus, i), numpy.round(i))

أمثلة:

5268685 (+1463262,-2401422) becomes 5300000 (+1500000,-2400000)
0.84312 +- 0.173124 becomes 0.84 +- 0.17

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

def removeExponent(decimal):
    digits = [str(n) for n in decimal.as_tuple().digits]
    length = len(digits)
    exponent = decimal.as_tuple().exponent
    if length <= -1 * exponent:
        zeros = -1 * exponent - length
        digits[0:0] = ["0."] + ["0"] * zeros
    elif 0 < -1 * exponent < length:
        digits.insert(exponent, ".")
    elif 0 <= exponent:
        digits.extend(["0"] * exponent)
    sign = []
    if decimal.as_tuple().sign == 1:
        sign = ["-"]
    print "".join(sign + digits)

المشكلة تحاول أن تتجول إلى شخصيات مهمة. لن تدور طريقة "Quantize ()" للعشرية من النقطة العشرية ، وتؤدي وظيفة "Round ()" دائمًا إلى إرجاع تعويم. لا أعرف ما إذا كانت هذه هي الأخطاء ، ولكن هذا يعني أن الطريقة الوحيدة لتوصيل أرقام النقطة العائمة الدقيقة اللانهائية هي تحليلها كقائمة أو سلسلة والقيام بالتقريب يدويًا. بمعنى آخر ، لا توجد إجابة عاقلة على هذا السؤال.

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