I am currently creating an application that allows users to input a weight and select their preferred unit, between kg and lbs. As of now, the database is written in Django and stores each user weight entry for a particular date as a string with 2 decimal points precision, such as,
weight_kg = models.DecimalField(max_digits=5, decimal_places=2, validators=[MinValueValidator(1)])
The problem I am facing is loss of data when changing between units. For example if a user chooses 165 with lbs
as the unit, I will store this in my database as 74.84
. When closing and reopening the page, it will fetch the data and attempt to restore it. Doing it, it will change 74.84kg
to 164.99lbs
. A loss of data, as we have no idea if the user chose 164.99lbs
or it was a rounding error.
I was starting to think that I made a mistake choosing to store the weights as precisely as the user inputted them (Precisely 2dp of precision in my Django field). Using numbers, the same problem will occur too,
let kg_to_lbs = 2.20462;
let lbs_to_kg = 0.453592;
((165 * lbs_to_kg) * kg_to_lbs) // 164.99....
Is their any best practices to prevent such issues?
Additional information,
- User input is restricted to 2 decimal points, 0 to 1000 using a spinbutton.
- The database stores the weights in
kg
, the client is responsible for converting them back to the unit used for display and converting them tokg
before sending them to the server for storage.
2
Answers
The issue you’re facing is a common problem in floating point arithmetic, which is notoriously difficult to handle accurately due to the way computers store and process numbers. The problem arises because the conversion between kg and lbs involves a non-integer ratio, which cannot be exactly represented as a floating point number.
The best approach to deal with this issue is to avoid floating point arithmetic altogether, and instead use a fixed-point representation. In this case, you can store the weight as an integer number of grams (since 1kg = 1000g), and then convert to kg or lbs when displaying the value to the user.
Here’s an example of how you can implement this in your Django model:
python
JavaScript
By using integers and rounding, you can ensure that the stored and restored values are as accurate as possible, and avoid the loss of data that occurs when using floating point numbers.
(a) Use the proper kilogram-to-pound factor. By international agreement and definition, a pound is .453 592 37 kilogram.
(b) Convert pounds to kilograms properly, by dividing by the kilogram-to-pound factor, rather than by multiplying by a rounded reciprocal (particularly one rounded to only six significant digits).
(c) Store the weight in finer units. Simply using pounds as the units instead of kilograms results, with the above, in a conversion that accurately recovers inputs up to at least 108 kilograms. However, there are multiple ways this can be done, including: