I am passing a simple float value in a dictionary from the server to client. When the float arrives at the client, it seems to have increased the number of decimal places (which technically is introducing accuracy that didn’t exist in the original value?).
If I then try rounding this back down to 5 places client side, it works for a print statement, but when setting the text property of a text component on the form the value reverts to a larger number of decimal places again.
Server code:
@anvil.server.callable
def return_long_test():
return {'longitude': 8.75781}
Client side code:
print("Calling return_long_test")
long_dict = anvil.server.call('return_long_test')
print(long_dict)
long = long_dict['longitude']
self.lbl_lng_test.text = long
App Console log:
Calling return_long_test
{'longitude': 8.757809999999999}
The label on the form displays:

If I use the round(long, 4) method in the client code, it successfully round back down to 4 places (although rounding to 5 or more places does nothing):
long_dict = anvil.server.call('return_long_test')
print(long_dict)
long = long_dict['longitude']
long_rounded = round(long, 4)
print(long_rounded)
self.lbl_lng_test.text = long_rounded
Console log:
Calling return_long_test
{‘longitude’: 8.757809999999999}
8.7578
Label on form:

Is this a quirk of the python / JS interface that I’m just not aware of?
Any why does it do it for this value but not if I return e.g. 42.56477 from the server method?
Calling return_long_test
{‘longitude’: 42.56477}
42.5648
I know this is probably something silly I’m missing, but I’ll be damned if I can work it out!
The underlying (binary-floating-point) value hasn’t changed. But it may be rendered slightly differently, behind the scenes, by different pieces of code.
Each one tries to convert the binary fraction to a “close-enough” decimal form, but since the correspondence isn’t exact, some will err on the high side, some on the low.
See What Every Programmer Should Know About Floating-Point Arithmetic .
The solution is not to round the (binary) number beforehand – in the result, the fraction is still binary, not decimal – but to round during the formatting (conversion to text).
Python has several different ways to accomplish this, e.g.,
self.lbl_lng_test.text = f'{long_rounded:.5f}'
Edit: see also 2.4.3. Formatted string literals
1 Like
That looks like a great document - I’ll read that!
Ah ha - makes sense - thanks 
The real value is actually coming in a query result from MongoDB, where I’ve done the rounding in the aggregation query itself - so I was quite confused as to how it was changing again. But I lacked the fundamental understanding of binary-floating-point objects, and knowing that the binary fraction is potentially going to be converted differently by the client-side code explains the behaviour exactly 
I suppose another way of guaranteeing how it displays would be to round it on the server and convert to string before returning to the client, avoid passing the binary-floating-point to the client at all. Which would be fine for things that are going straight into labels, but actually wouldn’t work for passing coords to then be worked with in the client anyway 
1 Like
I agree with Phil’s explanation, though I wouldn’t rely too heavily on the idea that the underlying binary value is guaranteed to remain unchanged.
When dealing with floats, there’s no absolute certainty that two values are truly identical, especially when a number is generated on the server, serialized, transmitted, and then deserialized on the client. Python and Skulpt may not use the exact same internal representation, and the serialization/deserialization step could introduce small differences in precision.
Rule 1: Never assume two floats are exactly equal. Always compare them using a tolerance.
Rule 2: What You See Is NOT What You Get. A float’s string representation may lose precision, or you may deliberately round it for clarity.
2 Likes