structure opened this issue on Aug 24, 2021 ยท 17 posts
structure posted Tue, 24 August 2021 at 2:40 AM Forum Coordinator
so using the following code I came across the "can't represent this decimal properly" feature.
I understand the reasoning for this, it has to do with the fact that decimals cannot be accurately represented by binary.
But lets say I need real precision ( for some engineering venture perhaps ). The results in blue are what I want, the results in red are what I am getting.
Is there a workaround in python to actually return the decimals I want?
from os.path import basename, dirname, exists, isfile, join, splitext
...
...
# make a new ( numbered ) copy of the original file
def uniquify( path, ext ):
file_name = path
if isfile(file_name):
expand = 0
while True:
expand += round( .1, 2 )
exp = "_" + str(expand)
new_file_name = file_name.split(ext)[0] + str(exp) + ext
if isfile(new_file_name):
continue
else:
return new_file_name
changing the .1 to 1/10 yields the same result
similarly the following code returns odd numbers also.
import numpy
for i in numpy.arange( 0.1, 0.9, 0.1 ):
print( i )
which returns
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7000000000000001
0.8
if I round the result:
for i in numpy.arange( 0.1, 0.9, 0.1 ):
print( round( i, 2 ) )
python returns the following ...
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
EDIT : a temporary workaround seems to be slicing the returned number like this
new_file_name = file_name.split(ext)[0] + str(exp[0:4]) + ext
Locked Out
odf posted Tue, 24 August 2021 at 4:17 AM
I'm not sure I understand the problem. If your application is to print version numbers, why represent them as floats, at all?
In general, to represent a number as a string rounded to a certain number of decimal places, say 3, you can use ("%.3f" % x)
.
for i in numpy.arange( 0.1, 0.9, 0.1 ):
print("%.1f" % i)
-- I'm not mad at you, just Westphalian.
bagginsbill posted Tue, 24 August 2021 at 7:32 AM
We could literally answer your questions about goofy floats but first let's decide if that rabbit hole is worth ANY of the grief. I suggest instead that you deal with version strings using the dedicated, properly designed features of Python for this. Have a look.
https://packaging.pypa.io/en/latest/version.html
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
bagginsbill posted Tue, 24 August 2021 at 7:34 AM
Oh crap. Right after I wrote that I discovered you have to install that "packaging" package and you might not be able to in Poser Python.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
bagginsbill posted Tue, 24 August 2021 at 7:46 AM
OK so after a quick experiment I conclude your problem is simply that adding .1 each time is a bad way to do as the slight inaccuracy of representing .1 accumulates. Instead, just iterate on an integer and divide by 10.
Example:
for i in range(100):
name = 'foo_copy_' + str(i/10) + '.bar'
print(name)
Works just fine
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
bagginsbill posted Tue, 24 August 2021 at 7:48 AM
Honestly, though, I don't know why you need 0.1, 0.2, 0.3 ... instead of just 1, 2, 3 ...
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
bagginsbill posted Tue, 24 August 2021 at 7:53 AM
I cleaned up your function - it can be much simpler.
# make a new ( numbered ) copy of the original file
def uniquify( path, ext ):
file_name = path + ext
i = 0
while isfile(file_name):
i += 1
file_name = path + "_copy_" + str(i/10) + ext
return file_name
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
adp001 posted Tue, 24 August 2021 at 8:33 AM
bagginsbill posted at 8:28AM Tue, 24 August 2021 - #4425960
I cleaned up your function - it can be much simpler.
# make a new ( numbered ) copy of the original file def uniquify( path, ext ): file_name = path + ext i = 0 while isfile(file_name): i += 1 file_name = path + "_copy_" + str(i/10) + ext return file_name
To make that work with Poser Versions != 12, you have to execute this first:
from __future__ import division
If you don't you will get garbage with this function.
But, as @odf said: The better way is to use Pythons format functions to convert numbers to strings.
adp001 posted Tue, 24 August 2021 at 8:43 AM
Python 3 (Poser 12) understands the old way of formatting strings with "%", and Python 2 (P11) can handle ".format(...)" strings preferred with Python 3.
bagginsbill posted Tue, 24 August 2021 at 11:26 AM
I'm not using Python 12 - but I am using Python 3 because 2 is dead.
Anyway - yes if you're in Python 2 (Poser 11 or below) then division by 10 needs to be i / 10.0.
And yes you can format umpteen ways
f'{i / 10:.1f}'
'%.1f' % (i / 10.0)
str(i / 10.0)
You pick which way you're comfortable with. I usually pick based on the simplest syntax, or the shortest amount of coding, which generally means fewer mistakes.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
adp001 posted Tue, 24 August 2021 at 2:35 PM
As far as I know, the forum is called "Python" and not "Python 3".
By the way, in the Poser universe there are significantly more Poser 11 users who need Python 2 than those who use Poser 12 with Python 3. And that's likely to continue for a while. As I see it, P12 will probably need another 3-5 years to become reasonably stable and usable. Which means Python 2 will continue to exist in the Poser universe....
Using Python standards (like format for displaying numbers) also has the advantage that there are less problems with conversions/changes. The current disaster regarding Python 3 incompatibility of most scripts should be incentive enough to do things differently in the future and stick to established standards.
odf posted Tue, 24 August 2021 at 6:35 PM
Thanks to this thread I learned that Python 3 has a decimal module.
-- I'm not mad at you, just Westphalian.
adp001 posted Tue, 24 August 2021 at 6:40 PM
https://docs.python.org/2.7/library/decimal.html
:) :) :)
odf posted Tue, 24 August 2021 at 6:41 PM
adp001 posted at 6:36PM Tue, 24 August 2021 - #4425963
bagginsbill posted at 8:28AM Tue, 24 August 2021 - #4425960
I cleaned up your function - it can be much simpler.
# make a new ( numbered ) copy of the original file def uniquify( path, ext ): file_name = path + ext i = 0 while isfile(file_name): i += 1 file_name = path + "_copy_" + str(i/10) + ext return file_name
To make that work with Poser Versions != 12, you have to execute this first:
from __future__ import division
If you don't you will get garbage with this function.
My preference is to use str(i / 10.0)
which will work correctly in both Python 2 and 3. Similarly, I like to use the //
in Python 2 because it won't break when one upgrades to 3.
-- I'm not mad at you, just Westphalian.
odf posted Tue, 24 August 2021 at 6:43 PM
adp001 posted at 6:42PM Tue, 24 August 2021 - #4426000
https://docs.python.org/2.7/library/decimal.html
:) :) :)
I didn't say that Python 2 didn't have it. It's just that the Python 3 manual was the first hit I found and I was too lazy to check if it was also available for Python 2.
-- I'm not mad at you, just Westphalian.
adp001 posted Tue, 24 August 2021 at 6:52 PM
odf posted at 6:43PM Tue, 24 August 2021 - #4426001
My preference is to use
str(i / 10.0)
which will work correctly in both Python 2 and 3. Similarly, I like to use the//
in Python 2 because it won't break when one upgrades to 3.
Yup, mine too. But it still makes more sense to write a formatted string like ... + "%1.1f" % [value] + ...
structure posted Tue, 24 August 2021 at 8:17 PM Forum Coordinator
Thanks for your feedback guys.
I have no intention of using flouting points in the example cited. I am actually using integers for that. However while writing the code for this I became curious as to how best to work with floating points. (I also tried the decimal module.)
I appreciate your responses.
p.s. @BagginsBill... Thanks for the tidy up
Locked Out