'Print the last rad in while loop in Ada

I wrote a program in Ada that ask user to put startprise, stopppris,step and procent, the the program will calculate and print the result.

My code is good,I have no problem with it, I can print all values except the last row.

The user will tell Folowing values:

Startprise:10.0

stopprise: 11.0

Step: 0.1

Procent: 10.0

The result should be like:

10.00 1.00 11.00

10.10 1.01 11.11

----etc

10.90 1.09 11.99

11.00 1.10 12.10

My code cann,t print the last row, while loop stop at 10.90 1.09 11.99, how I can fix it and print the last row:

enter code here

 with Ada.Text_IO; use Ada.Text_IO;
 with Ada.Float_Text_IO; use Ada.Float_Text_IO;


 procedure Main is
 startpris, stoppris,step:Float := 0.0 ;

procent: Float := -0.1;
priseftermoms, moms: Float;
p : Float := 100.0;
I : Integer:= 1;
J : Float := 0.0;
x : Float := -0.1;
index: Float := 0.0;

begin

while startpris <= J loop
  Put("Första Pris: ");
  Get(startpris);
if startpris < J then
 Put_Line (" Felaktigt värde!");
end if;
end loop;

while stoppris <= J or stoppris <= startpris loop
  Put("Sista Pris: ");
  Get(stoppris);
--stoppris := Float'Value(Get_Line);
if startpris > stoppris then
  Put_Line ("Felaktigt värde!");
  end if;
end loop;

while step <= J loop
  Put("Steg: ");
  Get(step);
  --step  := Float'Value (Get_Line);
  if step < J  then
  Put_Line (" Felaktigt värde!");
  end if;
end loop;

while procent < J or procent > p loop 
Put("Momsprocent: ");
Get(procent);
if procent <= x then
 Put_Line (" Felaktigt värde!");
end if;
if procent > P then
  Put_Line (" Felaktigt värde!");
end if;
end loop;
Put_Line("");
Put_Line("=================== Momstabell =====================");
Put_Line("Pris utan moms        Moms           Pris med moms");



 while stoppris >= startpris loop

  moms := (startpris * procent)/ p;
  priseftermoms := moms + startpris;
  put(startpris,fore=>1, aft => 2, exp=> 0);
  put("                  ");
  put(moms,fore=>1, aft => 2, exp=> 0);
  put("                ");
  put(priseftermoms,fore=>1, aft => 2,exp=> 0);
  Put_Line("");
  startpris := startpris + step;
 end loop;
end Main;


Solution 1:[1]

Your mistake is in thinking that computation with floating-point numbers (type Float) is exactly the same as computing with base-10 numbers with decimal parts. In a binary computer, floating-point numbers are represented in binary, base 2, which means that fractions such as 1/10, although written as 0.1 in the source code and in input text, are not represented exactly but with a small error that depends on the number of bits in the mantissa. So the program is actually computing with a value that is either 0.099...9x..., where the digit x is not 9, or 0.100...0x..., where the digit x is not zero. That is, the value in the computations is either slightly less than 0.1 or slightly more than 0.1.

This means that in your program, as the last while-loop adds "step" to "startpris", there is both an initial error (from the conversion of the input base-10 numbers into binary numbers) and a cumulative error (from the repeated addition of "step", with its initial error). The result is that after N iterations, the value of "startpris" is not exactly 10.0 + N*0.1, but something slightly different. So after 10 iterations, when you expect the loop to print out the values for "startpris" = 110.0, it may happen (and seems to happen) that the actual value of "startpris" is a little larger than 110.0. The while-loop condition is then False, and the loop is not executed for that value of "startpris".

To correct the program, you can either make the while-loop condition tolerant of this small error, for example as

while stoppris >= startpris - step/2.0 loop

or you can compute the integer number of steps that are needed, and then loop for that number of steps, for example as

steps : constant Natural := Natural ((stoppris - startpris) / step);
pris : Float;
...
for num_step in 0 .. steps loop
   pris := startpris + Float(num_step) * step;
   moms := (pris * procent) / p;
   ... and so on, using "pris" instead of "startpris".

Note that the conversion to Natural in the declaration of "steps" rounds the floating-point number to the nearest integer. This makes it tolerant to the small errors in the binary floating-point representation.

I recommend this second method, computing the number of steps and then using a for-loop.

There is a third solution, which is apt when dealing with amounts where the number of decimals is fixed, for example euros and cents written as xxx.yy, where yy is the number of cents. Then you can use the "decimal fixed-point types" of Ada to get exact calculations in base 10 with two decimals. But that is too long a subject for an SO answer...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1