Home > Software engineering >  While loop not stopping at float 2.00
While loop not stopping at float 2.00

Time:11-12

I'm a beginner trying to get this while loop so that the println table reads 2.00 15.000010, however it consistently stops on 1.9999993.

float weight = 60 ;
float height01 = 1.20 ;
float height02 = 2.00 ;

while( height01 < height02 ) {
    float BMI = ( weight / (height01 * height01) ) ;
    println( height01   " , "   BMI ) ;
    height01 = height01   0.02 ;
}

The output reads:

1.9999993 , 15.0000105

I've tried using Math.round(height01) in order to convert the float to an int but this seems to do absolutely nothing.

CodePudding user response:

There are many ways to deal with the lack of precision introduced by floating-point operations. One that may serve you well here is upscaling your float into the domain of integers, such that you can perform most calculations with said integer. For displaying the result, you can then divide and round, to provide a more consistently accurate variable:

float weight = 60 ;
int height01 = 120 ; //scaled by 100
int height02 = 200 ; //scaled by 100 (think centimeters!)

while( height01 < height02 ) {
    //100*100 in denominator, multiply numerator by 10000
    float BMI = ( (weight * 10000) / (height01 * height01) ) ;
    //scale our integer into a float (100F == (float) 100.0)
    println( (height01 / 100F)   " , "   BMI ) ;
    height01 = height01   2 ;
}

There are also many alternative solutions like using BigDecimal#scale, upscaling your float after calculations into an int and back, or perhaps even using double instead of float here (since it has double precision). However, the main takeaway is that floating-point arithmetic is accurate, but not perfectly precise. You will end up having to deal with thresholds or small tricks at times to make the output what you desire.

@geocodezip posted an even further solution that merits pointing out specifically:

String.format("%.2f", height01)

This will not truncate the decimal place, but will in fact round it. This may be suitable for your needs as a drop-in replacement as well.

CodePudding user response:

Answer from a C point-of-view which applies to this Java post.

A 32-bit float can exactly encode about 232 different values. 1.20 is not one of them. Finite float values are always a dyadic rational, some integer times a power of 2.
With float height01 = 1.20 ;, height01 has a nearby value of 1.2000000476837158203125

Adding 1.2000000476837158203125 often incurs a rounded sum. That sum is never 2.0.

1.2000000476837158203125000
1.2200000286102294921875000
1.2400000095367431640625000
...
1.9799993038177490234375000
1.9999992847442626953125000
2.0199992656707763671875000

To fix,

Use an integer loop count:

float weight = 60.0f;
float height01 = 1.20f;
float height02 = 2.00f;
float delta = 0.02f;
long n = lround((height02 - height01)/delta);

// iterate 0,1,2, ... n
for (long i = 0; i <= n; i  ) {
    float BMI = ( weight / (height01 * height01) ) ;
    println( height01   " , "   BMI ) ;
    height01 = height01   delta;
}

Or add tolerance to the limit detection of 1/2 the step

float weight = 60.0f ;
float height01 = 1.20f;
float height02 = 2.00f;
float delta = 0.02f;
float height02plus = height02   delta/2;

while( height01 <= height02plus) {
    float BMI = ( weight / (height01 * height01) ) ;
    println( height01   " , "   BMI ) ;
    height01 = height01   delta;
}

Use f suffix for float constants. It makes a difference.


Note: BMI is higher for taller individuals than shorter ones with the same body fat ratio..

CodePudding user response:

Try using the <= operator in your while loop.

while(height01 <= height02) {
...
}

As you have it, your loop stops before it reaches 2.0, you want it to actually reach that value first, before it stops.

  • Related