Python rounding float value : adapting code in other code. (loop, function)

python
dynamo

#1

EDIT: i found a new possible way perhaps. Jump to post 21 for that :slight_smile:

image
My Excel is dutch =AFRONDEN() means =ROUND()

The Python values:
value 1 = 13 decimals
value 2 = 100 + value 1 (it should still have 13 decimals, but it seems to round)

if i do the same in Excel it seems to make the same mistake …

maybe someone can help me or explain why this happens?


Need help with precision in python
#2

If you look at the amount of digits that are displayed this should help to identify the issue.

What seems to be happening is that it is limiting the displayed total numbers to 15(16 characters if you include the dot). This means the one with “10” at the start has 2 digits prior to the dot and 13 digits afterwards. Once you add 100 to it this means there is now 3 digits prior to the dot so therefore can only display 12 digits afterwards.


#3

Yeah, for some reason 15 digits is the default. but this way it makes an calculation error :frowning:

here i see its possible to exceed the limit to 17 digits: https://docs.microsoft.com/en-us/dotnet/api/system.double.tostring?redirectedfrom=MSDN&view=netframework-4.7.2#System_Double_ToString_System_String_

but i dont know if this is adaptable in python and if so, how?


#4

I haven’t seen your python code so i cannot comment about it, but have you tried just adding .ToString() to the end of where the number is set to see what the outcome is?

Eg:
Number.ToString()

I am unsure if the additional areas of what you have indicated can be added within the () for the tostring method, but you could try and see?


#5

I’m trying to calculate how much times a dimension is dividable by (value 1 + value 2). if it automaticly rounds the outcome of (value 1 + value 2) for me, i get wrong outcomes.


#6

It looks like, even though Dynamo is only displaying 15 digits, it is storing the float with full accuracy. See below:

dyn str vs float.dyn (18.3 KB)


#7

you are running into the limits of double precision floating pt numbers:

The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16). If a decimal string with at most 15 significant digits is converted to IEEE 754 double-precision representation, and then converted back to a decimal string with the same number of digits, the final result should match the original string. If an IEEE 754 double-precision number is converted to a decimal string with at least 17 significant digits, and then converted back to double-precision representation, the final result must match the original number.[1]

if you need more precision you will have to look into arbitrary precision numbers and math libraries.


#8

Hi @Schasfoortyoeri

How about displaying decimal places like this?


#9

@Kulkul that looks good! would it be possible to also calculate with it?

@kennyb6 made me this working code:

little explaination:

userinput: seam value = 700/65 in this case the 10.(alot decimals)
userinput: brick value = 100

in python brick = seam + brick 110. (alot decimals, in this case)

and because of the rounding somewhere in python it always gives me the else case instead of elif.


@Mostafa_El_Ayoubi we currently use the Data Shapes package to input number value’s (which always works if they are readable numbers)
would it be possible to let the user give the division? 700/65 is alot easyer than 10.7692307692308


#10

Yes! Could you describe more what you want to calculate.


#11

@kulkul i think the link in my last post was the best description. Also the script works, it Just seems not precise enough in case the user inputs alot decimals. In the example in the picture below it works (its calculating the dimension.below value based on dimension value)


#12

Hey,

if it automaticly rounds the outcome of (value 1 + value 2) for me, i get wrong outcomes.

Excuse me if i’m being stupid, but your number is going off to infinity…

So there has to be rounding?

I’m probably misunderstanding… But I tried to suggest (badly) in the previous thread, that it’s surprisingly hard to establish from the calculating values whether a calculated number will be correct to a specific decimal place.

My experience with your method is that it works for 99% of cases. I showed a graph which instead works off the calculated values, finds the surrounding correct numbers and compares them.

Apologies if I’ve gone off on a tangent.

Mark


#13

@Schasfoortyoeri You mean like this?


#14

@Kulkul no exactly, i ll give an example of what i m trying to achieve:

With the current code it gives me 65 X + R (which is not right.)


#15

please read the link I posted above. double precision math is not arbitrarily precise.
The key quote is:

The 53-bit significand precision gives from 15 to 17 [significant decimal digits](https://en.wikipedia.org/wiki/Significant_figures) precision (2−53 ≈ 1.11 × 10−16).


#16

Yes, i was allready affraid but hoped for a work around…

EDIT: Found somewhat a workaround, see below.


#17

The next piece of code by @kennyb6 works on whole numbers, i’m trying to adapt it to make it work with decimal numbers to. Python Dimension.Below = calculated value based on actual Dimension value(Maths)
at first i thought it wasnt possible, but now i got a ‘breakthrough’ :stuck_out_tongue:
the piece of code that i have works on a single string. and i want to adapt it to work with multiple dim values like the piece of code @kennyb6 wrote.
see attachments and code below.

Actual code that works:

# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

# Functions
def getForm(dim, brick, brickN, seam, seamN):
	val = round(dim.Value * ftmm)
	x = str(int(round(val / brick)))
	if val % brick == seam:
		form = x + brickN + " + " + seamN
	elif val % brick == brick - seam:
		form = x + brickN + " - " + seamN
	elif val % brick == 0:
		form = x + brickN
	else:
		form = str(int(val // brick)) + brickN + " + R"
	dim.Below = form
	return form


# The inputs to this node will be stored as a list in the IN variables.
allDims = UnwrapElement(IN[0])
brick = IN[1]
seam = IN[2]
brickN = IN[3]
seamN = IN[4]
ftmm = 304.8

forms = []
TransactionManager.Instance.EnsureInTransaction(doc)
for dims in allDims:
	if dims.Value == None:
		form = []
		for dim in dims.Segments:
			form.append(getForm(dim, brick, brickN, seam, seamN))
		forms.append(form)
	else:
		form = []
		form.append(getForm(dims, brick, brickN, seam, seamN))
		forms.append(form)
TransactionManager.Instance.TransactionTaskDone()

OUT = forms

The piece of code that i m trying to adapt:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import decimal
from decimal import *
getcontext().rounding = ROUND_HALF_UP
getcontext().prec = 12


# The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN

bS 	= IN[0]
brick 	= IN[1]
tolerance 	= IN[2]
dimValue	= IN[3]


D = decimal.Decimal

seam = str(D(bS) - D(brick))

bsLow = str(D(bS) - D(tolerance))
bsHigh = str(D(bS) + D(tolerance))

brickLow = str(D(brick) - D(tolerance))
brickHigh = str(D(brick) + D(tolerance))

seamLow = str(D(seam) - D(tolerance))	
seamHigh = str(D(seam) + D(tolerance))

toleranceLow = str( 0 - D(tolerance))
toleranceHigh = str(D(tolerance))

strVoeg = ''



km_in_maat = str(int(D(dimValue) / D(bS))) # aantal bS
rest = str(D(dimValue) % D(bS))

if   D(bsLow) <= D(rest) <= D(bsHigh):
	 	km_in_maat = str(D(km_in_maat) + 1)
		strVoeg = km_in_maat + 'K'
elif D(brickLow) <= D(rest) <= D(brickHigh):
		km_in_maat = str(D(km_in_maat) + 1)
		strVoeg = km_in_maat + 'K-V'
elif D(seamLow) <= D(rest) <= D(seamHigh):
		if km_in_maat == '0':
			strVoeg = 'R'							# 0K+V >>> R
		else:
			strVoeg = km_in_maat + 'K+V'
else:
	if km_in_maat == '0':
		strVoeg = 'R'
	elif D(toleranceLow) <= D(rest) <= D(toleranceHigh):
		strVoeg = km_in_maat + 'K'
	else:
		strVoeg = km_in_maat + 'K+R'
		


OUT =  strVoeg

Currently i m feeding it a single string. it needs some changes to work with dim values i think, i just dont know what to change. any help is greatly appreciated!


#18

if i m not clear please let me know, i find it hard to explain this kind of stuf :slight_smile: but if so, i ll try harder