Ask Your Question
1

Correct way to get an exact number of decimal digits (after the point)

asked 2020-01-05 17:24:39 +0100

geroyx gravatar image

If I type in

a = numerical_approx(1/14, digits=7)
print a
b=1/14
print b.n(digits=7)

I get

0.07142857
0.07142857

I count 8 digits after the point.
I wonder: How to get 7 digits as declared?

edit retag flag offensive close merge delete

3 Answers

Sort by ยป oldest newest most voted
2

answered 2020-01-08 16:26:40 +0100

dsejas gravatar image

updated 2020-04-04 13:46:36 +0100

Hello, @geroyx! The problem you have is that the digits parameter of the N() function actually means number of significant figures.

Updated answer: Sage has a round function, which does exactly what you want. For example,

a = round(1/14, ndigits=7)
print(a)

You will get

0.0714286

Old answer: At the time of posting my answer, I din't know if Sage had a built-in mechanism for dealing with number of digits after the decimal point, but here is a work-around that I posted. I keep it next for historical purposes. (I didn't have the time to mathematically check if this is correct, so maybe you should do it or ask somebody to verify it.)

def N2(x, n=7):
    d = floor(log(abs(x), 10)) + 1
    return N(x, digits=n+d)

You can check this works fine with:

N2(-100.25)
N2(1/14)
N2(0.0123, 10)

That should return the following:

-100.2500000
0.0714286
0.0123000000

(the exact number of digits after the decimal point.)

I hope this helps!

edit flag offensive delete link more

Comments

@dsejas OK, I understand. Seems your code has a little mistake. Suggestion:

def NumPrint(x, n):
    d = floor(log(abs(x), 10)) + 1
    return N(x, digits=n+d)#, d

print NumPrint(-100.25,2)
print NumPrint(1/14,7)
print NumPrint(0.0123, 4)
geroyx gravatar imagegeroyx ( 2020-01-09 19:28:26 +0100 )edit

Oops! Sorry, @geroyx. You're right. I mixed one previous version of my code with the final one. I am editing my answer right now. Thank you!

dsejas gravatar imagedsejas ( 2020-01-10 02:05:16 +0100 )edit
1

answered 2021-01-04 18:17:43 +0100

dan_fulea gravatar image

This is rather a discussion around the answer, not an answer going to the point. (Because "7 digits as declared" is not really clear. And sometimes, why not have more, when more can be easily given?!)


Let us first see what is numerical_approx doing in detail. A first word of warning is obtained when asking for the documentation of this function. We can ask for the doc string via

sage: a = 1/14
sage: a.numerical_approx?

Bur asking for more...

sage: a.numerical_approx??

we get

def numerical_approx(self, prec=None, digits=None, algorithm=None):
    """
    Return a numerical approximation of ``self`` with ``prec`` bits
    (or decimal ``digits``) of precision.

    No guarantee is made about the accuracy of the result.

    INPUT:

    - ``prec`` -- precision in bits

    - ``digits`` -- precision in decimal digits (only used if
      ``prec`` is not given)

    - ``algorithm`` -- which algorithm to use to compute this
      approximation (the accepted algorithms depend on the object)

    If neither ``prec`` nor ``digits`` is given, the default
    precision is 53 bits (roughly 16 digits).

    EXAMPLES::

        sage: (2/3).numerical_approx()
        0.666666666666667
        sage: pi.n(digits=10)
        3.141592654
        sage: pi.n(prec=20)
        3.1416

    TESTS:

    Check that :trac:`14778` is fixed::

        sage: (0).n(algorithm='foo')
        0.000000000000000
    """
    from sage.arith.numerical_approx import numerical_approx_generic
    if prec is None:
        prec = digits_to_bits(digits)
    return numerical_approx_generic(self, prec)

So the digits argument is taken and used to see how many bits "would correspond" to this digits precision. In our case, if we convert to a binary number the result...

sage: a = 1/14
sage: an = a.numerical_approx(digits=7)
sage: print('{:.50f}'.format(float(an)))
0.07142857182770967483520507812500000000000000000000

If we use the prec option instead for some sensible values...

sage: for prec in [15..35]:
....:     print(f'a.n(prec={prec}) is {float(a.n(prec=prec)):.50f}')
a.n(prec=15) is 0.07143020629882812500000000000000000000000000000000
a.n(prec=16) is 0.07142829895019531250000000000000000000000000000000
a.n(prec=17) is 0.07142829895019531250000000000000000000000000000000
a.n(prec=18) is 0.07142877578735351562500000000000000000000000000000
a.n(prec=19) is 0.07142853736877441406250000000000000000000000000000
a.n(prec=20) is 0.07142853736877441406250000000000000000000000000000
a.n(prec=21) is 0.07142859697341918945312500000000000000000000000000
a.n(prec=22) is 0.07142856717109680175781250000000000000000000000000
a.n(prec=23) is 0.07142856717109680175781250000000000000000000000000
a.n(prec=24) is 0.07142857462167739868164062500000000000000000000000
a.n(prec=25) is 0.07142857089638710021972656250000000000000000000000
a.n(prec=26) is 0.07142857089638710021972656250000000000000000000000
a.n(prec=27) is 0.07142857182770967483520507812500000000000000000000
a.n(prec=28) is 0.07142857136204838752746582031250000000000000000000
a.n(prec=29) is 0.07142857136204838752746582031250000000000000000000
a.n(prec=30) is 0.07142857147846370935440063476562500000000000000000
a.n(prec=31) is 0.07142857142025604844093322753906250000000000000000
a.n(prec=32) is 0.07142857142025604844093322753906250000000000000000
a.n(prec=33) is 0.07142857143480796366930007934570312500000000000000
a.n(prec=34) is 0.07142857142753200605511665344238281250000000000000
a.n(prec=35) is 0.07142857142753200605511665344238281250000000000000

So a.n(digits=7) uses the precision...

sage: IR = RealField(3000)
sage: for prec in [1..50]:
....:     if IR(a.n(prec=prec)) == IR(a.n(digits=7)):
....:         print(prec)
27

Note that the print of the a.n() objects is using the str or repr functionality implemented in the corresponding class, which is:

sage: a.n(prec=300)
0.0714285714285714285714285714285714285714285714285714285714285714285714285714285714285714286
sage: type(_)
<class 'sage.rings.real_mpfr.RealNumber'>
sage: a.n(digits=7)
0.07142857
sage: type(_)
<class 'sage.rings.real_mpfr.RealNumber'>

To have "the own print", i converted above to (the class / type of) IR or to float.


Now to the question. "How to get 7 digits as declared?"

We have always "more digits" if we want to, to display $7$ and only $7$ digits in a printed message, use the corresponding print functionality. We use as intermediate most simply the float converter.

sage: print(f'{float(a):.7f}')
0.0714286
sage: print(f'{RR(a):.7f}')
0.0714286

Note that the printed value is rounded. If this is not wanted, than let us go this way...

sage: import decimal
sage: d = decimal.Decimal(float(a.n(80)))
sage: d
Decimal('0.0714285714285714246063463406244409270584583282470703125')
sage: c = decimal.getcontext().copy()
sage: c.prec = int(7)
sage: c.rounding = decimal.ROUND_DOWN
sage: d.normalize(c)
Decimal('0.07142857')
sage: d.normalize(c).as_tuple()
DecimalTuple(sign=0, digits=(7, 1, 4, 2, 8, 5, 7), exponent=-8)

Well, we get $7$ significant decimals...

edit flag offensive delete link more
0

answered 2020-12-19 10:59:48 +0100

alxymitr gravatar image

updated 2020-12-23 07:44:47 +0100

def Ns(x, nd = 5):
        # round x to a given number of significant digits 
        d = ceil(log(abs(x), 10))
        return round(x, ndigits = -d + nd)
edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2020-01-05 17:24:39 +0100

Seen: 6,746 times

Last updated: Jan 04 '21