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.