[SOLVED] Precision issue when computing weight range given BMI range in Swift

Issue

I am making a BMI app. I have this code:

enum MathHelper {
  static func computeBMI(kg: Decimal, cm: Decimal) -> Decimal {
    return kg / cm / cm * 10_000
  }

  static func computeWeight(cm: Decimal, bmi: Decimal) -> Decimal {
    return bmi * cm * cm / 10_000
  }
  
  static func computeWeightRange(
    cm: Decimal,
    bmiRange: (from: Decimal, to: Decimal))
    -> (from: Decimal, to: Decimal)
  {
    let fromWeight = computeWeight(cm: cm, bmi: bmiRange.from)
    let toWeight = computeWeight(cm: cm, bmi: bmiRange.to)
    return (from: fromWeight, to: toWeight)
  }
}

Now I print the from/to pair, with 1 fraction length.

print(Decimal.FormatStyle.number.precision(.fractionLength(1)).format(from))
print(Decimal.FormatStyle.number.precision(.fractionLength(1)).format(to))

When I run this code with height 180 cm, BMI range from 18.5 to 24.9 (which is the BMI range for healthy weight), I got 59.9 – 80.7 weight range.

But this is not correct. The weight range should be 59.7-80.8. Because if I put in 59.7 and 80.8 into the computeBMI function and print result in 1 fraction length, it’s still within range 18.5-24.9.

Feel free to try it out. This is minimal reproducible code:


enum MathHelper {
  static func computeBMI(kg: Decimal, cm: Decimal) -> Decimal {
    return kg / cm / cm * 10_000
  }

  static func computeWeight(cm: Decimal, bmi: Decimal) -> Decimal {
    return bmi * cm * cm / 10_000
  }
  
  static func computeWeightRange(
    cm: Decimal,
    bmiRange: (from: Decimal, to: Decimal))
    -> (from: Decimal, to: Decimal)
  {
    let fromWeight = computeWeight(cm: cm, bmi: bmiRange.from)
    let toWeight = computeWeight(cm: cm, bmi: bmiRange.to)
    return (from: fromWeight, to: toWeight)
  }
}


func format(_ decimal: Decimal) -> String {
  return Decimal.FormatStyle.number.precision(.fractionLength(1)).format(decimal)
}


let height: Decimal = 180
let upperBMI: Decimal = 24.9
let computedUpperWeight = format(MathHelper.computeWeight(cm: height, bmi: upperBMI))
// this prints out 80.7, which is incorrect, because 80.8 is the correct answer (see below)
print("computed upper weight: \(computedUpperWeight)")

let correctUpperWeight: Decimal = 80.8
let bmiFromCorrectUpperWeight = format(MathHelper.computeBMI(kg: correctUpperWeight, cm: height))
// This prints out 24.9, which is still within the uppoer bound
print("BMI from correct upper weight \(bmiFromCorrectUpperWeight)")

Solution

While flanker’s argument is valid, BMI values are typically represented with 1 precision in real world application.

My recommendation is to adjust your BMI chart range by 0.05.

For example, with these 2 ranges (i quote from wikipedia).

  • Normal: 18.5-24.9
  • Overweight 25-29.9

You can represent your range in your code like this:

  • Normal: 18.45-24.95
  • Overweight 24.95-29.95

This should solve your problem.

Answered By – OMGPOP

Answer Checked By – Jay B. (BugsFixing Admin)

Leave a Reply

Your email address will not be published.