0

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.

4 Answers4

1

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

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

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.

Rogue
  • 11,105
  • 5
  • 45
  • 71
0

To clarify, I am presuming by "println table reads 2.00 15.000010" the OP actually meant "last println reads 2.00 15.000010".

To accomplish this, change "<" to "<=" and change the precision. Here it is in c (sorry, I hate java):

Mac_3.2.57$cat floatPlayground1.c
#include <stdio.h>

int main(void){
    float weight = 60;
    float height01 = 1.20;
    float height02 = 2.00;

    while(height01 <= height02){
        float BMI = (weight/(height01 * height01));
        printf("%10.2f, %10.6fi\n", height01, BMI);
        height01 = height01 + 0.02;
    }

    return(0);
}
Mac_3.2.57$cc floatPlayground1.c
Mac_3.2.57$./a.out 
      1.20,  41.666664
      1.22,  40.311741
      1.24,  39.021851
...
      1.96,  15.618504
      1.98,  15.304571
      2.00,  15.000010
Mac_3.2.57$
Andrew
  • 1
  • 4
  • 19
  • In general, `while(height01 <= height02){` is not a sufficient test. Try `float height01 = 1.3; ... height01 = height01 + 0.07;`. Loop stops earlier than desired. What would work more often is `float delta = 0.07; while(height01 <= height02 + delta/2){ ... height01 = height01 + delta;` – chux - Reinstate Monica Dec 17 '22 at 17:04
-2

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.

jtryon
  • 1
  • 3