Float to String: Problem with string length - ada

I would like to convert a float value to a String and created the following short example:
with Ada.Text_IO;
procedure Example is
A : constant Float := -1.234;
B : constant Float := 123_456.789;
C : constant Float := 987.654_321;
package Float_IO is new Ada.Text_IO.Float_IO (Num => Float);
String_Float_A : String := " ";
String_Float_B : String := " ";
String_Float_C : String := " ";
begin
Ada.Text_IO.Put_Line (Float'Image (A));
Ada.Text_IO.Put_Line (Float'Image (B));
Ada.Text_IO.Put_Line (Float'Image (C));
Float_IO.Put
(To => String_Float_A,
Item => A,
Aft => 2,
Exp => 0);
Float_IO.Put
(To => String_Float_B,
Item => B,
Aft => 2,
Exp => 0);
Float_IO.Put
(To => String_Float_C,
Item => C,
Aft => 2,
Exp => 0);
Ada.Text_IO.Put_Line (String_Float_A);
Ada.Text_IO.Put_Line (String_Float_B);
Ada.Text_IO.Put_Line (String_Float_C);
end Example;
My problem: I need to create the string variables before the call of the procedure Put with a sufficient length. How can this be done dynamically at runtime? Basically I need to figure out the number of digits before the dot. Then a sufficient string length would be: 1 (sign) + Number_Of_Dots + 1 (decimal separator) + Aft.

The number of digits before the dot of a decimal number can be computed calculating 1 plus the integer part of the common logarithm (base 10) of
the integer part of the number.
In Ada:
N := Integer (Float'Floor (Log (Float'Floor (abs X), 10.0))) + 1;
Your program must be with'ed with Ada.Numerics.Elementary_Functions.
You must add a space for the minus sign if the number is negative.
This formula does not work if the integer part is 0, but then N is obviously 1.

Your example uses Float, perhaps as a proxy for some more specific real type. If your actual data is better modeled as a decimal fixed point type, discussed here, don't overlook the convenience of Edited Output for Decimal Types, discussed here. The example below uses the string "+Z_ZZZ_ZZ9.99" to construct a picture of the desired output Image.
Console:
- 1.23
+ 123,456.79
+ 987.65
+1,000,000.00
Code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Editing; use Ada.Text_IO.Editing;
procedure Editing is
type Number is delta 0.000_001 digits 12;
type Numbers is array (Positive range <>) of Number;
package Number_Output is new Decimal_Output (Number);
Format_String : constant String := "+Z_ZZZ_ZZ9.99";
Format : constant Picture := To_Picture (Format_String);
Values : constant Numbers :=
(-1.234, 123_456.789, 987.654_321, Number'Last);
begin
for Value of Values loop
Put_Line (Number_Output.Image (Value, Format));
end loop;
end Editing;

You could make a function to do the job, something like this:
with Ada.Text_IO;
with Ada.Strings.Fixed;
procedure Marcello_Float is
function Image (Item : Float;
Aft : Ada.Text_IO.Field := Float'Digits - 1;
Exp : Ada.Text_IO.Field := 3) return String
is
package Float_IO is new Ada.Text_IO.Float_IO (Float);
Result : String (1 .. 128);
begin
Float_IO.Put (To => Result,
Item => Item,
Aft => Aft,
Exp => Exp);
return Ada.Strings.Fixed.Trim (Result,
Side => Ada.Strings.Both);
end Image;
A : constant Float := -1.234;
B : constant Float := 123_456.789;
C : constant Float := 987.654_321;
A_Image : constant String := Image (A, Aft => 2, Exp => 0);
B_Image : constant String := Image (B, Aft => 2, Exp => 0);
C_Image : constant String := Image (C, Aft => 2, Exp => 0);
use Ada.Text_IO;
begin
Put_Line (A'Image & " => " & A_Image);
Put_Line (B'Image & " => " & B_Image);
Put_Line (C'Image & " => " & C_Image);
end Marcello_Float;
I made Result ridiculously long. I recognise that to calculate an exact size would in fact answer your original question; just being lazy.

Related

Range check and Length check in Ada (SPARK Mode)

these last weeks I have been trying to learn the ADA language, to do it I made an exercise to reverse a string using recursion, however when I compile it with GNATProve it gives me several errors which I have not been able to solve, it would be of great help if you could guide me on how to solve them using Preconditions and Postconditions.
My code:
function String_Reverse(Str:String) return String with
Pre => Str'Length > 0 ,
Post => String_Reverse'Result'Length <= Str'Length;
function String_Reverse (Str : String) return String is
Result : String (Str'Range);
begin
if Str'Length = 1 then
Result := Str;
else
Result :=
String_Reverse (Str (Str'First + 1 .. Str'Last)) &
Str (Str'First);
end if;
return Result;
end String_Reverse;
Errors:
dth113.adb:18:69: low: range check might fail
18>| String_Reverse (Str (Str'First + 1 .. Str'Last)) &
19 | Str (Str'First);
reason for check: result of concatenation must fit in the target type of the assignment
possible fix: precondition of subprogram at line 8 should mention Str
8 | function String_Reverse(Str:String) return String with
| ^ here
dth113.adb:18:69: medium: length check might fail
18>| String_Reverse (Str (Str'First + 1 .. Str'Last)) &
19 | Str (Str'First);
reason for check: array must be of the appropriate length
possible fix: precondition of subprogram at line 8 should mention Str
8 | function String_Reverse(Str:String) return String with
| ^ here
I'm tried using Preconditons and Postconditions about the input Str length
Gnatprove appears to have some difficulty with concatenating arrays.
This below proves, using subtypes rather than pre- and post-conditions. Proving that the result is actually the reverse of the input string might be trickier!
Spec:
pragma SPARK_Mode;
function String_Reverse (Str : String) return String with
Pre => Str'Length > 0,
Post => String_Reverse'Result'Length = Str'Length;
Body:
pragma SPARK_Mode;
function String_Reverse (Str : String) return String is
subtype Result_String is String (Str'Range);
Result : Result_String;
begin
if Str'Length = 1 then
Result := Str;
else
declare
subtype Part_String is String (1 .. Str'Length - 1);
Reversed_Part : constant Part_String
:= String_Reverse (Str (Str'First + 1 .. Str'Last));
begin
Result := Reversed_Part & Str (Str'First);
end;
end if;
return Result;
end String_Reverse;
With a minor change you could handle zero-length strings as well.
Your program keeps appending the bulk of the string to your result. This makes a string much larger than the initial string parameter. You cannot fix this with improved preconditions or post conditions.
See the difference between your solution and the Reverse_String function shown below. In the solution below the size of the returned string cannot be the size of the function string in parameter because it is building the reversed string with each recursion and the size of the string cannot be determined until all recursions complete.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
function Reverse_String(S : in String; Idx : Positive) return String is
begin
if Idx < S'Last then
return Reverse_String(S, Idx + 1) & S(Idx);
else
return S(Idx) & "";
end if;
end Reverse_String;
S1 : String := "Hello World";
s2 : String := "1234567890";
N1 : Integer := 12345;
N3 : Integer;
begin
Put_Line(S1 & " : " & Reverse_String(S1, 1));
Put_Line(S2 & " : " & Reverse_String(S2, 1));
N3 := Integer'Value(Reverse_String(N1'Image, 1));
Put_Line(N1'Image & " : " & N3'Image);
end Main;
This version of Reverse_String keeps incrementing the index of the input string and concatenating that character at the end of all the characters yet to reverse.
The Reverse_String's else clause appends an empty string to the last character so that it is viewed by the compiler as a string and not a character because S(Idx) is a single character and is not a string, but concatenating S(Idx) with an empty string results in a string.
Here is another implementation
function String_Reverse (Str : String) return String is
Result : String (Str'Range) := Str;
begin
for I in reverse Str'Range loop
Result(Str'Last - I + Result'First) := Str(I);
end loop;
return Result;
end String_Reverse;
SPARK seems to be happier with expression functions than with regular functions in many cases. I don't know if it will make a difference, but you could try rewriting the body of your function as
function String_Reverse (Str : String) return String is
(if Str'Length = 1 then
Str
else
String_Reverse (Str (Str'First + 1 .. Str'Last) ) & Str (Str'First) );
(I hope I've got the parentheses balanced.)
Typically one allows null strings, and the function becomes something like
function Reversed (S : in String) return String with
Post => Reversed'Result'Length = S'Length;
function Reversed (S : in String) return String is
(if S'Length = 0 then ""
else Reversed (S (S'first + 1 .. S'Last) & S (S'First) );
(Hint: "Ada" is a woman's name, not an acronym. GNATProve is a static-analysis tool, not a compiler.)

How to write a population-growth table in Ada?

** I want my code to display the following table for the population but it seems that my code is not
even close to this. So how can I make my code right? my biggest problem here is using a range-based
for loop. This table shows the population for A and B and their population growth(in percent) and
asks the user to enter some data and when the population b bypass population a then the loop should
stop. The sample code:**
Enter start year: 1976
Enter country A's population (in million): 200
Enter country A's population increase (in%): 10.0
Enter country B population (in million): 100
Enter country B population increase (in%): 50.0
Year Increase A Population A Increase B Population B
1976 ---- 200.00 ------ 100.00
1977 20.00 220.00 50.00 150.00
1978 22.00 242.00 75.00 225.00
1979 24.20 266.20 112.50 337.50
In 1979, country B bypassed country A in population.
My code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Population is
x,Population_A, Population_B:Float;
Growth_A, Growth_B, Growth_1,Growth_2: Float;
begin
Put("Write in the year: "); Get(x);
Put("Population_A: "); Get(Population_A);
Put("Population_B: "); Get(Population_B);
Put("Population grow for A: "); Get(Growth_A);
Put("Population growth for B: "); Get(Growth_B); New_Line;
Put("Year Growth A Population A Growth B Population B ");
for I in 1900..2018 loop
Put(Integer(I)); New_Line;
while Population_B < Population_A loop
Growth_1 := Growth_A +1.0;
Growth_2 := Growth_B +1.0;
Put(Growth_1, 13,2,0); New_Line;
Put(Population_A + (Population_A * Growth_A/100.0),15,2,0); New_Line;
Put(Growth_2, 17,2,0); New_Line;
Put(Population_B + (Population_B * Growth_B/100.0),19,2,0);
end loop;
end loop;
end Population;
Your problem is indeed using a range-based for loop. There are other forms.
I’d probably go for something like
-- read in the data
-- check that there is actually a solution
-- (B’s population not already greater than A’s)
Year := -- value read from data, e.g. 1976
loop
Year := Year + 1;
Population_Of_A := -- calculate it
Population_Of_B := -- calculate it
exit when Population_Of_B > Population_Of_A;
end loop;
You need a more complete concept of the problem you are trying to solve.
The problem assumes a constant population growth rate for each country. While this assumption simplifies the problem it is far from realistic.
Either country A or country B could start with the smaller initial population. If the country starting with the smaller population also has the lowest population growth rate the smaller country will never surpass the larger country in population. How will your program deal with the possibility that either country could initially have the larger population?
The following program deals with some of these problems but not all of them. Perhaps you can get an idea how to deal with all the problems by studying the following program.
with Ada.Text_Io; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Main is
Year : Positive;
type Country (Id : Character) is record
Population : Float;
Rate : Float;
Increase : Float;
end record;
type Country_Access is access all Country;
Country_A : aliased Country('A');
Country_B : Aliased Country('B');
Smaller : Country_Access;
Larger : Country_Access;
procedure Display(Yr : Positive; Cnt_A : Country; Cnt_B : Country) is
begin
Put(Item => Yr, Width => 4);
Put(Item => Cnt_A.Increase, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_B.Increase, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
end Display;
begin
Put("Enter start year: ");
Get(Year);
Put("Enter country A's population (in million): ");
Get(Country_A.Population);
Put("Enter country A's population increase (in %): ");
get(Country_A.Rate);
Country_A.Rate := (Country_A.Rate / 100.0);
Put("Enter country B's population (in million): ");
Get(Country_B.Population);
Put("Enter country B's population increase (in %): ");
Get(Country_B.Rate);
Country_B.Rate := (Country_B.Rate / 100.0);
if Country_A.Population < Country_B.Population then
Smaller := Country_A'Access;
Larger := Country_B'Access;
else
Smaller := Country_B'Access;
Larger := Country_A'Access;
end if;
Put_Line("Year Increase A Population A Increase B Population B");
Put(Item => Year, Width => 4);
Put(" ----------");
Put(Item => Country_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put(" ----------");
Put(Item => Country_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
while Smaller.Population <= Larger.Population loop
Year := Year + 1;
Country_A.Increase := Country_A.Population * Country_A.Rate;
Country_A.Population := Country_A.Population + Country_A.Increase;
Country_B.Increase := Country_B.Population * Country_B.Rate;
Country_B.Population := Country_B.Population + Country_B.Increase;
Display(Yr => Year,
Cnt_A => Country_A,
Cnt_B => Country_B);
end loop;
Put_Line("In" & Year'Image & " country " & Smaller.Id &
" bypassed country " &
Larger.Id & " in population.");
end Main;
EDIT:
Following is a version of the above program that does not use access types.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure No_Access is
Year : Positive;
type Country is record
Population : Float;
Rate : Float;
Increase : Float;
end record;
Country_A : Country;
Country_B : Country;
procedure Increase
(Yr : in out Positive; A : in out Country; B : in out Country)
is
begin
Yr := Yr + 1;
A.Increase := A.Population * A.Rate;
A.Population := A.Population + A.Increase;
B.Increase := B.Population * B.Rate;
B.Population := B.Population + B.Increase;
Put (Item => Yr, Width => 4);
Put (Item => A.Increase, Aft => 2, Fore => 8, Exp => 0);
Put (Item => A.Population, Aft => 2, Fore => 8, Exp => 0);
Put (Item => B.Increase, Aft => 2, Fore => 8, Exp => 0);
Put (Item => B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
end Increase;
begin
Put ("Enter start year: ");
Get (Year);
Put ("Enter country A's population (in million): ");
Get (Country_A.Population);
Put ("Enter country A's population increase (in %): ");
Get (Country_A.Rate);
Country_A.Rate := (Country_A.Rate / 100.0);
Put ("Enter country B's population (in million): ");
Get (Country_B.Population);
Put ("Enter country B's population increase (in %): ");
Get (Country_B.Rate);
Country_B.Rate := (Country_B.Rate / 100.0);
Put_Line ("Year Increase A Population A Increase B Population B");
Put (Item => Year, Width => 4);
Put (" ----------");
Put (Item => Country_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put (" ----------");
Put (Item => Country_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
if Country_A.Population < Country_B.Population then
while Country_A.Population <= Country_B.Population loop
Increase (Year, Country_A, Country_B);
end loop;
Put_Line
("In" & Year'Image & " country A bypassed country B in population.");
else
while Country_B.Population <= Country_A.Population loop
Increase (Year, Country_A, Country_B);
end loop;
Put_Line
("In" & Year'Image & " country B bypassed country A in population.");
end if;
end No_Access;

Subtype Mark Required in this context - Ada

I'm working to implement a prime decomposition function in Ada. I need to return a Vector from calc_prime_numbers. I'm trying to store that vector in Y. However, whenever I compile, the compiler is saying prime.adb:40:07: subtype mark required in this context. I'm not sure what that means. What does subtype required mean? How do I fix it?
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Containers.Vectors;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Containers;
procedure Prime is
package Integer_Vectors is new Vectors(Natural, Integer);
function Is_Prime(I : Integer) return Boolean is
J : Integer := 2;
begin
for J in 2 .. I-1 loop
if I mod J = 0 then
return False;
end if;
end loop;
return True;
end Is_Prime;
function calc_prime_numbers(n : Integer) return Integer_Vectors.Vector is
i : Integer := 0;
m : Integer;
Numbers : Integer_Vectors.Vector;
begin
m := n + 1;
while (true) loop
i:=i + 1;
if Is_Prime(i) then
Integer_Vectors.Append(Numbers, i);
Put(Integer'Image(i) & " + ");
end if;
if i = m then
exit;
end if;
end loop;
New_Line;
return Numbers;
end calc_prime_numbers;
X : Integer;
Y : Integer_Vectors; — line 40
begin
while (true) loop
Get(X);
if Is_Prime(X) then
Put(Integer'Image(X) & " is prime.");
else
Put(Integer'Image(X) & " is not prime.");
end if;
New_Line;
Y := calc_prime_numbers(X); — line 40
end loop;
end Prime;
Your line number in the error message don't match the code you pasted, and you don't indicate where line 40 is, so I'll have to guess:
you instantiate a package called Integer_Vectors. Later you declare a variable Y : Integer_Vectors;. So the compiler complains because it expects a type for the variable whereas you provided the name of a package.

How to use wildcard string in systemverilog case statement

Case #1:
module try;
string inp = "my_var";
initial begin
$display("Here we go!");
case (inp)
"my_var" : $display("my_var");
default : $display("default");
endcase
end
endmodule
Output is my_var
Case #2
module try;
string inp = "my_var";
initial begin
$display("Here we go!");
case (inp)
"*var*" : $display("*var*");
default : $display("default");
endcase
end
endmodule
Output is default.
Is it possible to get a hit with wildcard search in a case statement?
SystemVerilog does not have any string regular expression matching methods built into the standard. The UVM has a package that has a uvm_re_match() function. You can import the UVM package to get access to this function even if you do not use any other UVM testbench features. Some simulators, such as ModelSim/Questa have these routines built in as an extension to SystemVerilog so that you can do
module try;
string inp = "my_var";
initial begin
$display("Here we go!");
case (1)
inp.match("*var*") : $display("*var*");
default : $display("default");
endcase
end
endmodule
I found a work-around :
function string match(string s1,s2);
int l1,l2;
l1 = s1.len();
l2 = s2.len();
match = 0 ;
if( l2 > l1 )
return 0;
for(int i = 0;i < l1 - l2 + 1; i ++)
if( s1.substr(i,i+l2 -1) == s2)
return s2;
endfunction
module try;
string target_id = "abc.def.ddr4_0";
string inp = "ddr4_0";
string processed_inp;
initial begin
$display("Here we go!");
for(int i=0;i<2;i++) begin
if (i == 1)begin
inp = "ddr4_1";
target_id = "abc.def.ddr4_1";
end
processed_inp = match(target_id, inp);
$display("input to case = %0s", processed_inp);
case (processed_inp)
"ddr4_0" : $display("ddr4_0 captured!");
"ddr4_1" : $display("ddr4_1 captured!");
default : $display("default");
endcase
end
end
endmodule
Output:
Here we go!
input to case = ddr4_0
ddr4_0 captured!
input to case = ddr4_1
ddr4_1 captured!
PS : Found this solution on the www.
Cannot find the link right now. Will post the reference soon.

Sql Return Statment Getting encountered symbol error

This store procedure is returning this error however i cant see find the problem:
Error(20,1): PLS-00103: Encountered the symbol "END" when expecting
one of the following: . ( * # % & = - + ; < / > at in is mod
remainder not rem <> or != or ~= >= <= <> and or
like like2 like4 likec between || multiset member submultiset
create or replace procedure SP_CurrancyOfProject(V_AssyId number) as
temp number := 0;
begin
Select Scope_ID
into Temp
from tbl_Revisions
where Assy_U_ID = V_AssyId;
Select Project_ID
into temp
from tbl_Scope
Where Scope_U_ID = temp;
Select Currancy_Multipier
into temp
from tbl_Currancy
where Project_ID = temp;
return temp;
end;
You Cannot return value from a procedure.
you should use FUNCTION to return the value.
create or replace FUNCTION FN_CurrancyOfProject(V_AssyId number) RETURN NUMBER
as
temp NUMBER := 0;
BEGIN
-- YOUR CODE LOGIC for substituting temp variable.
return temp;
END;

Resources