Avoid rounding float in python - python

I have rather unusual request, but I hope someone might be able to help.
I am appending floats as values to numpy array.
I would like to make sure that float does not get rounded when its decimal part ends in 0.
For example:
I would like float 31.30 to stay 31.30 in the array, but what I am experiencing now is that it gets set to 31.3.
The reason why I want to make sure float does not change is that later on I am splitting it to two integers(31.30 to 31 and 30) and it is of critical importance for me that both integers have the 'complete' values.
Is there any way to get around this? I tried with making these floats strings, but my problem is that I need to compare the arrays with these floats and numpy.array_equal() does not seem to work for arrays of strings...
Any help will be greatly appreciated,
Best wishes

Since 31.3 and 31.30 are exactly the same number, there is no way to distinguish them. But I guess you don't need to anyway. This seems more like a formatting issue. If you always expect two digits after the dot:
from math import floor
x = 31.3
whole_part = int(floor(x))
two_after_dot = int(floor(x*100))-whole_part*100
print(whole_part, two_after_dot)
Output:
31 30
If this actually isn't the case and the number of digits after the dot should vary while also keeping varying numbers of trailing zeros, then you cannot use numeric types. Use strings from the very beginning instead.

When I ran into a similar problem because of converting millisecond references to microseconds, I had to convert to a string and loop over the string adding the needed 0's until the length of the string was correct. Then when the value was converted back to integer, the calculations worked.
The data will be passed to strptime as as string
vals = vals.split('.') # a fractional part of the seconds
nofrag, frag = vals
length = len(frag)
# This converts the fractional string to microseconds, given unknown precision
if length > 6:
frag = frag(0:5)
else:
while length < 6:
frag = frag + '0'
length += 1
# strptime requires even seconds with microseconds added later
nofrag_dt = DT.datetime.strptime(nofrag, '%Y%m%d %H%M%S')
dt = nofrag_dt.replace(microsecond=int(frag))
return dt

Related

Convert Python Series of strings into float with 18 decimals

I have the following pandas Series:
my_series = ['150000000000000000000000', '45064744242514231410', '2618611848503168287542', '7673975728717793369']
Every number in the list has 18 decimal places (that's what dictates what number exactly it is, prior to seeing any formatting).
my_series[0], therefore, is 150,000.000000000000000000 (one hundred and fifty thousand).
my_series[1], therefore, is 45.064744242514231410 (fourty-five...).
And so on.
I basically want Python to recognize the strings and tunr them into the correct float for me to make calculations with thie Series later.
I don't need to print the correct formatted number, rather, have Pythoin recognize it's a 150,000 instead of a 1,500,000,000 and so on.
Example for my_series[2] of what the corrrect float would be:
2,618.61
My current code:
[float("{:.18f}".format(int(item) for item in my_series))]
Which yields me the following error:
TypeError: unsupported format string passed to generator.__format__
How do I format the strings in the Series according to my requirements above and get the correct float?
You can convert the string to float and then apply formatting.
my_series = ['150000000000000000000000', '45064744242514231410',
'2618611848503168287542', '7673975728717793369']
["{:,.2f}".format(float(item)/10**18) for item in my_series]
['150,000.00', '45.06', '2,618.61', '7.67']
Note that this may lose some precision when converting the string to float.
If this is a problem to you, then you may want to use either
Separate the integer part and decimal part and combine them when printing
Use Decimal class
After a few iterations, I think I understand what OP was going for, so I changed my example. OP does not seem to be worried about loss of precision and was getting value errors (probably due to invalid fields coming in as part of the Series). I've modified my sample to be close to how it would happen in Pandas by adding some deliberately fake inputs.
my_series = [
"not a number",
"",
"150000000000000000000000",
"45064744242514231410",
"2618611848503168287542",
"7673975728717793369",
]
def convert_to_float(number):
float_string = None
my_float = None
try:
float_string = f"{int(number[:-18])}.{number[-18:]}"
my_float = float(float_string)
except ValueError as e:
print(e)
return None
return my_float
numbers = list(map(convert_to_float, my_series))
for num in numbers:
if num:
print(f"{num :.18f}")

0's in the beginning are being skipped & I'm not sure how to fix it

The output value is not including the 0's in the beginning, can someone help me fix the problem?
def bitwiseOR(P, Q):
return bin(P | Q)
bitwiseOR(0b01010111, 0b00111000)
OUTPUT: '0b1111111'
The leading zeroes are just for representation, so you can utilize Format Specification Mini-Language to display them as you wish:
Format string:
# Includes 0b prefix
0{length} Pad leading zeroes so total length is length
def bitwiseOR(P, Q, length=10):
return format(P | Q, f'#0{length}b')
x = bitwiseOR(0b01010111, 0b00111000)
# 0b01111111
print(x)
Leading zeros are a property of the string you produce, not the number. So, for example, if you're looking for a way to make the following two calls produce different results, that's not possible:1
bitwiseOR(0b01010111, 0b00111000)
bitwiseOR( 0b1010111, 0b111000)
However, if you can provide the number of digits separately, then you can do this using the format() function. It accepts a second argument which lets you customize how the number is printed out using the format spec. Based on that spec, you can print a number padded with zeros to a given width like this:
>>> format(127, '#010b')
'0b01111111'
Here the code consists of four pieces:
# means apply the 0b prefix at the beginning
0 means pad with leading zeros
10 means the total length of the resulting string should be at least 10 characters
b means to print the number in binary
You can tweak the format code to produce your desired string length, or even take the length from a variable.
1Well... technically there is a way to make Python re-read its own source code and possibly produce different results that way, but that's not useful in any real program, it's only useful if you want to learn something about how the Python interpreter works.

Convert a decimal number into a byte size of 8 bit array

x = 64
var_in_bin_format = bin(64)
print(var_in_bin_format)
#Output
#0b1000000
#Desired Output -- > should always be in 8 bit format
#0b01000000
def call_another_api(var_in_bin_format):
pass
In Python, I need to call an API that expects its parameter to be always in 8 bit format regardless of the value of the decimal number?
I am not that good in bit manipulation so I am thinking if there is something I can do here?
How can I do this? I cannot use the format() function as it will convert the value into a string representation and the API that I am calling will alert me that it is not in the correct format.
Even though you say that you can't use format() because it returns a string, I'm going to post this because that's also what bin() does. bin(x) is equivalent to format(x, '#b'). I'd guess that you haven't added the '#', which means you won't have '0b' leading the value.
The Python 3 documentation for bin() actually gives a pretty strong hint about how you might do this, using format instead.
If you know that the value passed will not be negative, you can use the format string '#010b':
format(x, '#010b')
Breaking this down:
'b' means that the number will be a string binary representation.
'10' means that the entire string will be 10 characters long, two for '0b' and 8 for the value.
'0' makes it pad with '0' instead of ' '.
'#' will add the '0b' prefix, as done by bin().
Note that this assumes that the number is an integer in the range [0, 255]. Integers outside this range will generate valid representations, but will not match the format expected, and may have a leading '-'. Objects of type float can not be converted with the 'b' format code. I assume that these are not problems, given what your intended output is, but it might be a good idea to add an explicit check to throw a ValueError if the value is less than 0 or greater than 255.
If you're in Python 3.6+, you could also use f-strings:
f'{x:#010b}'
Is is not possible to convert all decimal numbers to 8 bit. You can only convert numbers from 0 to 255 in 8 bits.

Python: Finding the length of a float with zeros at the end

I have several float values that have necessary zeros at the ends.
One number that I have is 0.0013790.
When finding the length of this, I get 8 when I should be getting 9, since the zero at the end is dropped. I can not use .format(), since some numbers are shorter than others and there is no concrete length that I want them set to. If I had a float that was seven digits long after the decimal and set the format to 8, I would get an extra zero which should NOT belong there.
I can not afford to have my program adding zeros through format when they are not always necessary, since some numbers will be shorter than others. How do I find the actual length of these numbers when a zero is at the end?
I can not make an if statement that checks if the number .endswith 0, because it never does. The zero is always dropped! I am already checking the length of the string of the float and still the zero is dropped! Many numbers will not end with zero, so I can not simply add one to the length found. Please help!
Numbers to test:
When inputting _, you should get _. If you can get the below to work along with some other numbers, please give me the solution. I've been racking at my brain for hours!! Thanks.
WANTED RESULTS: 0.12345 -> 7, 0.123450 -> 8, 0.1234500 -> 9.
UPDATE:
Thank you for your solutions, but the numbers are not inputs. I have them set to the eval() function, since I have roughly 1000 variables that need to be accessed dynamically from a websocket. Values are retrieved just fine, but if I am not mistaken, eval() defaults to float. Switching it from float to string has not done me much good, since I am guessing that eval() is always a float. Any solutions??
You need to store your values as strings if you want to track length independent of the value of the float.
Floating point values have no length, and trailing 0s do not affect the value so they produce identical floats. This means after it gets defined, there is no way to determine whether 0.12345 was defined using 0.12345 or 0.12345000000.
0.12345 is 0.123450 # True
0.12345 is 0.1234500 # True
len(0.12345) # TypeError: object of type 'float' has no len()
Everything works fine for the string representation of those floats:
"0.12345" is "0.123450" # False
"0.12345" is "0.1234500" # False
len("0.12345") # 7
Thus you should store these values as strings, and convert them to float when necessary.
If you first convert the input to a number, and then to a string, you'll lose any insignificant digits.
If you are asking the user to enter the value:
>>> foo = input('Enter the number here: ')
Enter the number here: 0.0013790
>>> len(foo)
9
If you are using Python 2, make sure you use raw_input and not input
As long as you don't cast the value to a float, you should get correct values for len().
Then we have to store the float as a string value. Following lines may be answer to the question where it is a default behaviour.
mynum = input('Enter your number: ')
print('Hello', mynum)
print(len(mynum))

randint with leading zero's

I want to generate numbers from 00000 to 99999.
with
number=randint(0,99999)
I only generate values without leading zero's, of course, a 23 instead of a 00023.
Is there a trick to generate always 5 digit-values in the sense of %05d or do I really need to play a python-string-trick to fill the missing 0s at front in case len() < 5?
Thanks for reading and helping,
B
You will have to do a python-string-trick since an integer, per se, does not have leading zeroes
number="%05d" % randint(0,99999)
The numbers generated by randint are integers. Integers are integers and will be printed without leading zeroes.
If you want a string representation, which can have leading zeroes, try:
str(randint(0, 99999)).rjust(5, "0")
Alternatively, str(randint(0, 99999)).zfill(5), which provides slightly better performance than string formatting (20%) and str.rjust (1%).
randint generates integers. Those are simple numbers without any inherent visual representation. The leading zeros would only be visible if you create strings from those numbers (and thus another representation).
Thus, you you have to use a strung function to have leading zeros (and have to deal with those strings later on). E.g. it's not possible to do any calculations afterwards. To create these strings you can do something like
number = "%05d" % random.randint(0,99999)
The gist of all that is that an integer is not the same as a string, even if they look similar.
>>> '12345' == 12345
False
For python, you're generating a bunch of numbers, only when you print it / display it is it converted to string and thus, it can have padding.
You can as well store your number as a formatted string:
number="%05d" % random.randint(0,9999)

Categories