I'm taking a Python course at Udacity, and I'm trying to work this out for myself without looking at the answer. Perhaps you can give me a hint for my logic?
Below are the instructions and what I have so far. We haven't learned conditional statements yet, so I can't use those. We've only learned how to assign/print a variable, strings, indexing strings, sub-sequences, and .find. They just introduced the str command in this final exercise.
# Given a variable, x, that stores the
# value of any decimal number, write Python
# code that prints out the nearest whole
# number to x.
# If x is exactly half way between two
# whole numbers, round up, so
# 3.5 rounds to 4 and 2.5 rounds to 3.
# You may assume x is not negative.
# Hint: The str function can convert any number into a string.
# eg str(89) converts the number 89 to the string '89'
# Along with the str function, this problem can be solved
# using just the information introduced in unit 1.
# x = 3.14159
# >>> 3 (not 3.0)
# x = 27.63
# >>> 28 (not 28.0)
# x = 3.5
# >>> 4 (not 4.0)
x = 3.54159
#ENTER CODE BELOW HERE
x = str(x)
dec = x.find('.')
tenth = dec + 1
print x[0:dec]
////
So this gets me to print the characters up to the decimal point, but I can't figure out how to have the computer check whether "tenth" is > 4 or < 5 and print out something according to the answer.
I figured I could probably get far enough for it to return a -1 if "tenth" wasn't > 4, but I don't know how I can get it to print x[0:dec] if it's < 5 and x[0:dec]+1 if it's > 4.
:/
Could someone please give me a nudge in the right direction?
This is a weird restriction, but you could do this:
x = str(x)
dec_index = x.find('.')
tenth_index = dec_index + 1
tenth_place = x[tenth_index] # will be a string of length 1
should_round_up = 5 + tenth_place.find('5') + tenth_place.find('6') + tenth_place.find('7') + tenth_place.find('8') + tenth_place.find('9')
print int(x[0:dec_index]) + should_round_up
What we do is look at the tenths place. Since .find() returns -1 if the argument isn't found, the sum of the .find() calls will be -4 if if the tenths place is 5, 6, 7, 8, or 9 (since one of the .find() calls will succeed and return 0), but will be -5 if the tenths place is 0, 1, 2, 3, or 4. We add 5 to that, so that should_round_up equals 1 if we should round up, and 0 otherwise. Add that to the whole number part, and we're done.
That said, if you weren't subject to this artificial restriction, you would do:
print round(x)
And move on with your life.
judging by the accepted answer you only expects floats so that is pretty trivial to solve:
x = 3.54159
# split on .
a, b = str(x).split(".")
# cast left side to int and add result of test for right side being greater or equal to 5
print(int(a) + (int(b) >= 5))
(int(b) > 5) will be either 1 or 0 i.e True/False so we either add 1 when right side is > .5 or flooring when it's < .5 and adding 0.
If you were doing it mathematically you just need to print(int(x+.5)), anything >= .5 will mean x will be rounded up and floored when it is < .5.
x = 3.54159
# split on .
a, b = str(x).split(".")
# cast left side to int and add result of test for right side being greater or equal to 5
print(int(a) + (int(b[0]) >= 5))
# above code will not work with 3.14567 and the number with having two or more digits after decimal
I think it's easier...
x = x + 0.5
intPart, decPart = str(x).split(".")
print intPart
Examples:
If x = 1, then it will become 1.5 and intPart will be 1.
If x = 1.1, then it will become 1.6 and intPart will be 1.
If x = 1.6, then it will become 2.1 and intPart will be 2.
Note: it will only work for positive numbers.
This code will round numbers to the nearest whole
without using conditionals
You can do it this way
x = 3.54159
x = x + 0.5 # This automatically takes care of the rounding
str_x = str(x) # Converting number x to string
dp = str_x.find('.') # Finding decimal point index
print str_x[:dp] # Printing upto but excluding decimal point
I did the same course at Udacity. solved it using the following code:
y = str(x)
decimal = y.find('.')
y_increment = y[decimal+1:]
print decimal
print y_increment
# Section below finds >5
check5 = y_increment.find('5',0,1)
check6 = y_increment.find('6',0,1)
check7 = y_increment.find('7',0,1)
check8 = y_increment.find('8',0,1)
check9 = y_increment.find('9',0,1)
yes_increment = (check5 + 1) + (check6 + 1) + (check7 + 1) + (check8 + 1) + (check9 + 1)
print check5, check6, check7, check8, check9
#Calculate rounding up
z = x + (yes_increment)
z = str(z)
final_decimal = z.find('.')
print z[:final_decimal]
Related
Say I have an arbitrary float x = 123.123456 and want to remove the last n decimal digits from the float i.e. n = 1 then x = 123.12345, n = 2 then x = 123.1234 and so on. How can this be achieved in python?
This will do the trick you are asking, but be mindful of the issues with floating point numbers.
# let's cut 2 digits
n = 2
# naively we can do this
f = 123.123456
short_f = float(str(f)[:-n])
# but watch out for floating point error
f = 1.2 - 1.0 # f should be 0.2 but is actually 0.19999999999999996
short_f = float(str(f)[:-n]) # so this gives 0.199999999999999
This sounds like an XY problem, maybe you are looking for round or string formatting.
I know that you can avoid getting outputted, for example, 1e-10 using '{:.10f}'.format(1e-10), but I would adjust to take the decimal places as a variable (the 10f and e-10) depending on what the the value after e is...
I've used this code to find the value of decimal points needed
a = 6.789e-05
b = str(a)
if b[5] == 'e':
if b[7] == '0':
decimal_places = int(b[8])
else:
decimal_places = int(b[7]+b[8])
But I am struggling to figure out how I could no adjust the decimal places of the variable a using what I've found? As replacing '10f' with the decimal_places variable doesn't work... any help appreciated!
I've written a simple function to get number precission:
def precision(num):
n = str(num).split('e')
if len(n) == 1:
return 0
x = 0 if len(n[0].split('.')) == 1 else len(n[0].split('.')[1])
return x + abs(int(n[1]))
Then you can use string formatting to print your number to desired precission:
a = 6.789e-05
b = 6e-05
c = 3.5
d = 5
print('{num:.{precision}f}'.format(num=a, precision=precision(a))) # 0.00006789
print('{num:.{precision}f}'.format(num=b, precision=precision(b))) # 0.00006
print('{num:.{precision}f}'.format(num=c, precision=precision(c))) # 3.5
print('{num:.{precision}f}'.format(num=d, precision=precision(d))) # 5
I'm processing a.CSV file in python which has a floating type field.
This field has to be modified such that it has at least 4 decimal points and max 8 decimal points of precision.
Example:
input: 5.15
output: 5.1500
input: -12.129999998
output: -12.12999999
What I'm currently doing:
#The field to be modifed is present at index 3 in list temp
dotIndex = temp[3].find('.') + 1
latLen = len(temp[3])-1
if (latLen) - (dotIndex) > 8:
temp[3] = temp[3][0:dotIndex+4]
elif (latLen) - (dotIndex) < 4:
temp[3] = temp[3][0:latLen] + (4 - (latLen - (dotIndex))) * '0'
Is there a better way to write this code to improve performance ?
this should work:
temp[3] = "{:.4f}".format(float(temp[3]))
Considering your comment and the fact you want it truncated, here you go:
n = len(temp[3].split('.')[1])
if n < 4:
temp[3] = "{:.4f}".format(float(temp[3]))
elif n > 8:
parts = temp[3].split('.')
temp[3] = parts[0]+"."+parts[1][:4]
If you're truncating, not rounding, you could use something like this:
def truncate_to_eight(val):
return '{:.8f}'.format((int(val * 10**8))/(10.0**8))
Multiplying by 10 to the power of 8, taking the integer part, and then dividing by 10 to the power 8 gets you the truncation required. Note however, this will always return a value with 8 decimal places - so 5.15 becomes 5.15000000.
You would use this by saying, for example:
rounded = truncate_to_eight(temp[3])
I want to get the length of a string including a part of the string that represents its own length without padding or using structs or anything like that that forces fixed lengths.
So for example I want to be able to take this string as input:
"A string|"
And return this:
"A string|11"
On the basis of the OP tolerating such an approach (and to provide an implementation technique for the eventual python answer), here's a solution in Java.
final String s = "A String|";
int n = s.length(); // `length()` returns the length of the string.
String t; // the result
do {
t = s + n; // append the stringified n to the original string
if (n == t.length()){
return t; // string length no longer changing; we're good.
}
n = t.length(); // n must hold the total length
} while (true); // round again
The problem of, course, is that in appending n, the string length changes. But luckily, the length only ever increases or stays the same. So it will converge very quickly: due to the logarithmic nature of the length of n. In this particular case, the attempted values of n are 9, 10, and 11. And that's a pernicious case.
A simple solution is :
def addlength(string):
n1=len(string)
n2=len(str(n1))+n1
n2 += len(str(n2))-len(str(n1)) # a carry can arise
return string+str(n2)
Since a possible carry will increase the length by at most one unit.
Examples :
In [2]: addlength('a'*8)
Out[2]: 'aaaaaaaa9'
In [3]: addlength('a'*9)
Out[3]: 'aaaaaaaaa11'
In [4]: addlength('a'*99)
Out[4]: 'aaaaa...aaa102'
In [5]: addlength('a'*999)
Out[5]: 'aaaa...aaa1003'
Here is a simple python port of Bathsheba's answer :
def str_len(s):
n = len(s)
t = ''
while True:
t = s + str(n)
if n == len(t):
return t
n = len(t)
This is a much more clever and simple way than anything I was thinking of trying!
Suppose you had s = 'abcdefgh|, On the first pass through, t = 'abcdefgh|9
Since n != len(t) ( which is now 10 ) it goes through again : t = 'abcdefgh|' + str(n) and str(n)='10' so you have abcdefgh|10 which is still not quite right! Now n=len(t) which is finally n=11 you get it right then. Pretty clever solution!
It is a tricky one, but I think I've figured it out.
Done in a hurry in Python 2.7, please fully test - this should handle strings up to 998 characters:
import sys
orig = sys.argv[1]
origLen = len(orig)
if (origLen >= 98):
extra = str(origLen + 3)
elif (origLen >= 8):
extra = str(origLen + 2)
else:
extra = str(origLen + 1)
final = orig + extra
print final
Results of very brief testing
C:\Users\PH\Desktop>python test.py "tiny|"
tiny|6
C:\Users\PH\Desktop>python test.py "myString|"
myString|11
C:\Users\PH\Desktop>python test.py "myStringWith98Characters.........................................................................|"
myStringWith98Characters.........................................................................|101
Just find the length of the string. Then iterate through each value of the number of digits the length of the resulting string can possibly have. While iterating, check if the sum of the number of digits to be appended and the initial string length is equal to the length of the resulting string.
def get_length(s):
s = s + "|"
result = ""
len_s = len(s)
i = 1
while True:
candidate = len_s + i
if len(str(candidate)) == i:
result = s + str(len_s + i)
break
i += 1
This code gives the result.
I used a few var, but at the end it shows the output you want:
def len_s(s):
s = s + '|'
b = len(s)
z = s + str(b)
length = len(z)
new_s = s + str(length)
new_len = len(new_s)
return s + str(new_len)
s = "A string"
print len_s(s)
Here's a direct equation for this (so it's not necessary to construct the string). If s is the string, then the length of the string including the length of the appended length will be:
L1 = len(s) + 1 + int(log10(len(s) + 1 + int(log10(len(s)))))
The idea here is that a direct calculation is only problematic when the appended length will push the length past a power of ten; that is, at 9, 98, 99, 997, 998, 999, 9996, etc. To work this through, 1 + int(log10(len(s))) is the number of digits in the length of s. If we add that to len(s), then 9->10, 98->100, 99->101, etc, but still 8->9, 97->99, etc, so we can push past the power of ten exactly as needed. That is, adding this produces a number with the correct number of digits after the addition. Then do the log again to find the length of that number and that's the answer.
To test this:
from math import log10
def find_length(s):
L1 = len(s) + 1 + int(log10(len(s) + 1 + int(log10(len(s)))))
return L1
# test, just looking at lengths around 10**n
for i in range(9):
for j in range(30):
L = abs(10**i - j + 10) + 1
s = "a"*L
x0 = find_length(s)
new0 = s+`x0`
if len(new0)!=x0:
print "error", len(s), x0, log10(len(s)), log10(x0)
Here is the example which is bothering me:
>>> x = decimal.Decimal('0.0001')
>>> print x.normalize()
>>> print x.normalize().to_eng_string()
0.0001
0.0001
Is there a way to have engineering notation for representing mili (10e-3) and micro (10e-6)?
Here's a function that does things explicitly, and also has support for using SI suffixes for the exponent:
def eng_string( x, format='%s', si=False):
'''
Returns float/int value <x> formatted in a simplified engineering format -
using an exponent that is a multiple of 3.
format: printf-style string used to format the value before the exponent.
si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
e-9 etc.
E.g. with format='%.2f':
1.23e-08 => 12.30e-9
123 => 123.00
1230.0 => 1.23e3
-1230000.0 => -1.23e6
and with si=True:
1230.0 => 1.23k
-1230000.0 => -1.23M
'''
sign = ''
if x < 0:
x = -x
sign = '-'
exp = int( math.floor( math.log10( x)))
exp3 = exp - ( exp % 3)
x3 = x / ( 10 ** exp3)
if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
exp3_text = 'yzafpnum kMGTPEZY'[ ( exp3 - (-24)) / 3]
elif exp3 == 0:
exp3_text = ''
else:
exp3_text = 'e%s' % exp3
return ( '%s'+format+'%s') % ( sign, x3, exp3_text)
EDIT:
Matplotlib implemented the engineering formatter, so one option is to directly use Matplotlibs formatter, e.g.:
import matplotlib as mpl
formatter = mpl.ticker.EngFormatter()
formatter(10000)
result: '10 k'
Original answer:
Based on Julian Smith's excellent answer (and this answer), I changed the function to improve on the following points:
Python3 compatible (integer division)
Compatible for 0 input
Rounding to significant number of digits, by default 3, no trailing zeros printed
so here's the updated function:
import math
def eng_string( x, sig_figs=3, si=True):
"""
Returns float/int value <x> formatted in a simplified engineering format -
using an exponent that is a multiple of 3.
sig_figs: number of significant figures
si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
e-9 etc.
"""
x = float(x)
sign = ''
if x < 0:
x = -x
sign = '-'
if x == 0:
exp = 0
exp3 = 0
x3 = 0
else:
exp = int(math.floor(math.log10( x )))
exp3 = exp - ( exp % 3)
x3 = x / ( 10 ** exp3)
x3 = round( x3, -int( math.floor(math.log10( x3 )) - (sig_figs-1)) )
if x3 == int(x3): # prevent from displaying .0
x3 = int(x3)
if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
exp3_text = 'yzafpnum kMGTPEZY'[ exp3 // 3 + 8]
elif exp3 == 0:
exp3_text = ''
else:
exp3_text = 'e%s' % exp3
return ( '%s%s%s') % ( sign, x3, exp3_text)
The decimal module is following the Decimal Arithmetic Specification, which states:
This is outdated - see below
to-scientific-string – conversion to numeric string
[...]
The coefficient is first converted to a string in base ten using the characters 0 through 9 with no leading zeros (except if its value is zero, in which case a single 0 character is used).
Next, the adjusted exponent is calculated; this is the exponent, plus the number of characters in the converted coefficient, less one. That is, exponent+(clength-1), where clength is the length of the coefficient in decimal digits.
If the exponent is less than or equal to zero and the adjusted exponent is greater than or equal to -6, the number will be converted
to a character form without using exponential notation.
[...]
to-engineering-string – conversion to numeric string
This operation converts a number to a string, using engineering
notation if an exponent is needed.
The conversion exactly follows the rules for conversion to scientific
numeric string except in the case of finite numbers where exponential
notation is used. In this case, the converted exponent is adjusted to be a multiple of three (engineering notation) by positioning the decimal point with one, two, or three characters preceding it (that is, the part before the decimal point will range from 1 through 999).
This may require the addition of either one or two trailing zeros.
If after the adjustment the decimal point would not be followed by a digit then it is not added. If the final exponent is zero then no indicator letter and exponent is suffixed.
Examples:
For each abstract representation [sign, coefficient, exponent] on the left, the resulting string is shown on the right.
Representation
String
[0,123,1]
"1.23E+3"
[0,123,3]
"123E+3"
[0,123,-10]
"12.3E-9"
[1,123,-12]
"-123E-12"
[0,7,-7]
"700E-9"
[0,7,1]
"70"
Or, in other words:
>>> for n in (10 ** e for e in range(-1, -8, -1)):
... d = Decimal(str(n))
... print d.to_eng_string()
...
0.1
0.01
0.001
0.0001
0.00001
0.000001
100E-9
I realize that this is an old thread, but it does come near the top of a search for python engineering notation and it seems prudent to have this information located here.
I am an engineer who likes the "engineering 101" engineering units. I don't even like designations such as 0.1uF, I want that to read 100nF. I played with the Decimal class and didn't really like its behavior over the range of possible values, so I rolled a package called engineering_notation that is pip-installable.
pip install engineering_notation
From within Python:
>>> from engineering_notation import EngNumber
>>> EngNumber('1000000')
1M
>>> EngNumber(1000000)
1M
>>> EngNumber(1000000.0)
1M
>>> EngNumber('0.1u')
100n
>>> EngNumber('1000m')
1
This package also supports comparisons and other simple numerical operations.
https://github.com/slightlynybbled/engineering_notation
The «full» quote shows what is wrong!
The decimal module is indeed following the proprietary (IBM) Decimal Arithmetic Specification.
Quoting this IBM specification in its entirety clearly shows what is wrong with decimal.to_eng_string() (emphasis added):
to-engineering-string – conversion to numeric string
This operation converts a number to a string, using engineering
notation if an exponent is needed.
The conversion exactly follows the rules for conversion to scientific
numeric string except in the case of finite numbers where exponential
notation is used. In this case, the converted exponent is adjusted to be a multiple of three (engineering notation) by positioning the decimal point with one, two, or three characters preceding it (that is, the part before the decimal point will range from 1 through 999). This may require the addition of either one or two trailing zeros.
If after the adjustment the decimal point would not be followed by a digit then it is not added. If the final exponent is zero then no indicator letter and exponent is suffixed.
This proprietary IBM specification actually admits to not applying the engineering notation for numbers with an infinite decimal representation, for which ordinary scientific notation is used instead! This is obviously incorrect behaviour for which a Python bug report was opened.
Solution
from math import floor, log10
def powerise10(x):
""" Returns x as a*10**b with 0 <= a < 10
"""
if x == 0: return 0,0
Neg = x < 0
if Neg: x = -x
a = 1.0 * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if Neg: a = -a
return a,b
def eng(x):
"""Return a string representing x in an engineer friendly notation"""
a,b = powerise10(x)
if -3 < b < 3: return "%.4g" % x
a = a * 10**(b % 3)
b = b - b % 3
return "%.4gE%s" % (a,b)
Source: https://code.activestate.com/recipes/578238-engineering-notation/
Test result
>>> eng(0.0001)
100E-6
Like the answers above, but a bit more compact:
from math import log10, floor
def eng_format(x,precision=3):
"""Returns string in engineering format, i.e. 100.1e-3"""
x = float(x) # inplace copy
if x == 0:
a,b = 0,0
else:
sgn = 1.0 if x > 0 else -1.0
x = abs(x)
a = sgn * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if -3 < b < 3:
return ("%." + str(precision) + "g") % x
else:
a = a * 10**(b % 3)
b = b - b % 3
return ("%." + str(precision) + "gE%s") % (a,b)
Trial:
In [10]: eng_format(-1.2345e-4,precision=5)
Out[10]: '-123.45E-6'