skip to Main Content

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,

  1. User input is restricted to 2 decimal points, 0 to 1000 using a spinbutton.
  2. The database stores the weights in kg, the client is responsible for converting them back to the unit used for display and converting them to kg before sending them to the server for storage.

2

Answers


  1. 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

    weight_g = models.IntegerField(validators=[MinValueValidator(1)])
    
    def get_weight_kg(self):
        return self.weight_g / 1000
    
    def get_weight_lbs(self):
        return self.weight_g * 0.00220462  
    

    JavaScript

    let kg_to_g = 1000;
    let lbs_to_g = 453.592;
    
    let user_weight_kg = 165;
    let user_weight_lbs = user_weight_kg * kg_to_g / lbs_to_g;
    
    let stored_weight_g = Math.round(user_weight_kg * kg_to_g);
    let restored_weight_kg = stored_weight_g / kg_to_g;
    let restored_weight_lbs = stored_weight_g / lbs_to_g;
    

    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.

    Login or Signup to reply.
  2. (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:

    • Store the weight as pounds with two digits after the decimal point, as mentioned above.
    • Store the weight as kilograms with more than two digits after the decimal point.
    • Store the weight as an integer number of grams (mathematically equivalent to the above).
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search