generate random numbers truncated to 2 decimal places

10,819

Solution 1

A float cannot be truncated (or rounded) to 2 decimal digits, because there are many values with 2 decimal digits that just cannot be represented exactly as an IEEE double.

If you really want what you say you want, you need to use a type with exact precision, like Decimal.

Of course there are downsides to doing that—the most obvious one for numpy users being that you will have to use dtype=object, with all of the compactness and performance implications.

But it's the only way to actually do what you asked for.

Most likely, what you actually want to do is either Joran Beasley's answer (leave them untruncated, and just round at print-out time) or something similar to Lauritz V. Thaulow's answer (get the closest approximation you can, then use explicit epsilon checks everywhere).

Alternatively, you can do implicitly fixed-point arithmetic, as David Heffernan suggests in a comment: Generate random integers between 0 and 50, keep them as integers within numpy, and just format them as fixed point decimals and/or convert to Decimal when necessary (e.g., for printing results). This gives you all of the advantages of Decimal without the costs… although it does open an obvious window to create new bugs by forgetting to shift 2 places somewhere.

Solution 2

How about this?

np.random.randint(0, 50, size=(50,1)).astype("float") / 100

That is, create random integers between 0 and 50, and divide by 100.

EDIT:

As made clear in the comments, this will not give you exact two-digit decimals to work with, due to the nature of float representations in memory. It may look like you have the exact float 0.1 in your array, but it definitely isn't exactly 0.1. But it is very very close, and you can get it closer by using a "double" datatype instead.

You can postpone this problem by just keeping the numbers as integers, and remember that they're to be divided by 100 when you use them.

hundreds = random.randint(0, 50, size=(50, 1))

Then at least the roundoff won't happen until at the last minute (or maybe not at all, if the numerator of the equation is a multiple of the denominator).

Solution 3

decimals are not truncated to 2 decimal places ever ... however their string representation maybe

import numpy as np
rs = np.random.RandomState(123456)
set = rs.uniform(size=(50,1))*0.5

print ["%0.2d"%val for val in set]
Share:
10,819
mtigger
Author by

mtigger

-

Updated on June 04, 2022

Comments

  • mtigger
    mtigger almost 2 years

    I would like to generate uniformly distributed random numbers between 0 and 0.5, but truncated to 2 decimal places.

    without the truncation, I know this is done by

    import numpy as np
    rs = np.random.RandomState(123456)
    set = rs.uniform(size=(50,1))*0.5
    

    could anyone help me with suggestions on how to generate random numbers up to 2 d.p. only? Thanks!

  • Lauritz V. Thaulow
    Lauritz V. Thaulow almost 11 years
    @Joran Wow, that's fast feedback. Have you tested this? My tests all return only two or one decimal places.
  • abarnert
    abarnert almost 11 years
    @LauritzV.Thaulow: There are many values with two decimal places that cannot be represented exactly as a float (IEEE double). So, you may get the closest float to the value you wanted, and it may even repr and str as the value you wanted… but when you do arithmetic with it, or just compare it with ==, it may not be the value you wanted. You have to use an exact-precision type like Decimal if you really want two decimal places.
  • abarnert
    abarnert almost 11 years
    Decimals can be truncated to 2 decimal places; it's floats that can't.
  • aestrivex
    aestrivex almost 11 years
    Also, this doesn't solve the problem because sampling from a normal distribute won't generate integers (though you could round the distribution so it does). A more general idea of a solution along these lines would be (for x~Norm(params)), int(round(x*100))/100.
  • Joran Beasley
    Joran Beasley almost 11 years
    meh semantics :P (but yeah I know Decimal is an exact representation type :P)
  • mtigger
    mtigger almost 11 years
    thank you, this cleared up some misconceptions I had about floats and decimals.
  • abarnert
    abarnert almost 11 years
  • Lauritz V. Thaulow
    Lauritz V. Thaulow almost 11 years
    Why not just use np.around(rs.uniform(size=(50,2)) * 0.5, decimals=2) then? It's faster, it works the same, and has the same technical drawback that my solution has, as noted in the docs: Results may also be surprising due to the inexact representation of decimal fractions in the IEEE floating point standard.