在python中很好地表示一个浮点数 [英] Nicely representing a floating-point number in python

查看:406
本文介绍了在python中很好地表示一个浮点数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个浮点数字表示为一个四舍五入为有效数字的数字,并且从不使用指数格式。本质上,我想显示任何浮点数,并确保它看起来不错。

这个问题有几个部分:




  • 我需要能够指定
    有效位数。
  • 有效位数
    需要是变量,不能用$ a
    完成字符串格式
    操作符
    。我已经纠正了;字符串格式化操作符可以做到这一点。

  • 我需要它被
    的人所期望的方式四舍五入,而不是
    像1.999999999999



我已经想出了一个这样做的方法,尽管它看起来像一个工作轮,并不完美。 (最高精度为15位有效数字)。

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

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

有没有更好的方法来做到这一点?为什么Python没有内置的函数呢?

解决方案

看起来没有内置的字符串格式化技巧(1)打印第一位有效数字出现在十进制小数点后面的浮点数,(2)不以科学记数法打印浮点数。所以,这就留下了手动的字符串操作。



下面我使用十进制模块从浮点数中提取十进制数字。
使用 float_to_decimal 函数将float转换为 Decimal 对象。显而易见的方式 decimal.Decimal(str(f))是错误的,因为 str(f)可能会丢失有效数字。


float_to_decimal 已从

一旦十进制数字作为int的元组获得,下面的代码做了明显的事情:切断所需数量的显着数字,必要时将其舍入,将数字连接成一个字符串,粘贴一个符号,根据需要向左或向右放置一个小数点和零点。



最后我们会发现一些我用来测试 f 函数的例子。

 进位十进制

def float_to_decimal(f):
#http://docs.python.org /library/decimal.html#decimal-faq
将浮点数转换为十进制数,不丢失任何信息
n,d = f.as_integer_ratio()
分子,分母=十进制.decimal(n),decimal.Decimal(d)
ctx = decimal.Context(prec = 60)
结果= ctx.divide(分子,分母)
而ctx.flags [decimal .include]:
ctx.flags [decimal.Inexact] = False
ctx.prec * = 2
result = ctx.divide(分子,分母)
返回结果

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)
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])))
#将结果舍入
len(digits)> sigfig和digits [sigfig]> = 5:result + = 1
result = list(str(result))
#四舍五入可以改变结果的长度
#如果是,调整shift
shift + = len(result)-sigfig
#将结果的len重置为sigfig
结果=结果[:sigfig]
如果转换> = sigfig-1:
#在结束时附加更多的零
结果+ = ['0'] *(shift-sigfig + 1)
elif 0 <= shift:
#将小数点放在数位之间
result.insert(shift + 1,'。 ')
else:

assert(shift <0)
result = ['0。'] + ['0'] *( - shift -1)+结果
如果符号:
result.insert(0,' - ')
return''.join(resul t)

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'),$ b $ (34500000000000000000000,17,'34500000000000000000000'),
('34500000000000000000000',17,'34500000000000000000000'),
(756867,7,'756867.0'),
]

为数字,sigfig,在测试中回答:
尝试:
结果= f(数字,sigfig)
assert(result ==答案)
打印(结果)
除了AssertionError:
print('Error',number,sigfig,result,answer)
pre>

I want to represent a floating-point number as a string rounded to some number of significant digits, and never using the exponential format. Essentially, I want to display any floating-point number and make sure it “looks nice”.

There are several parts to this problem:

I've figured out one way of doing this, though it looks like a work-round and it's not quite perfect. (The maximum precision is 15 significant digits.)

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

Is there a better way to do this? Why doesn't Python have a built-in function for this?

解决方案

It appears there is no built-in string formatting trick which allows you to (1) print floats whose first significant digit appears after the 15th decimal place and (2) not in scientific notation. So that leaves manual string manipulation.

Below I use the decimal module to extract the decimal digits from the float. The float_to_decimal function is used to convert the float to a Decimal object. The obvious way decimal.Decimal(str(f)) is wrong because str(f) can lose significant digits.

float_to_decimal was lifted from the decimal module's documentation.

Once the decimal digits are obtained as a tuple of ints, the code below does the obvious thing: chop off the desired number of sigificant digits, round up if necessary, join the digits together into a string, tack on a sign, place a decimal point and zeros to the left or right as appropriate.

At the bottom you'll find a few cases I used to test the f function.

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)

这篇关于在python中很好地表示一个浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆