Differences
This shows you the differences between two versions of the page.
ftoc_-_a_fahrenheit_to_celsius_converter [2011/12/16 21:08] thomaslocke |
ftoc_-_a_fahrenheit_to_celsius_converter [2012/03/06 22:20] (current) cvanvliet fixed a few grammar erros |
||
---|---|---|---|
Line 7: | Line 7: | ||
Before we start, we need to do a little preparation. First create the following directory/file structure somewhere on your computer: | Before we start, we need to do a little preparation. First create the following directory/file structure somewhere on your computer: | ||
- | $ mkdir FtoC | + | <code bash> |
- | $ cd FtoC | + | $ mkdir FtoC |
- | $ mkdir exe objects | + | $ cd FtoC |
- | $ touch ftoc.adb ftoc.gpr | + | $ mkdir exe objects |
+ | $ touch ftoc.adb ftoc.gpr | ||
+ | </code> | ||
The above should leave you with the following contents in the //FtoC// directory: | The above should leave you with the following contents in the //FtoC// directory: | ||
Line 85: | Line 87: | ||
===== Compile And Run FtoC ===== | ===== Compile And Run FtoC ===== | ||
- | If you use the excellent [[http://libre.adacore.com/libre/|GPS IDE]], the compiling is a simple matter of pressing //F4// to compile the project, and //Shift+F2// to execute it. If you're not using [[http://libre.adacore.com/libre/|GPS]], then do this instead: | + | If you use the excellent [[http://libre.adacore.com/libre/tools/gps/|GPS IDE]], the compiling is a simple matter of pressing //F4// to compile the project, and //Shift+F2// to execute it. If you're not using [[http://libre.adacore.com/libre/tools/gps/|GPS]], then do this instead: |
- | $ gnatmake -P ftoc.gpr | + | <code bash> |
+ | $ gnatmake -P ftoc.gpr | ||
+ | </code> | ||
You should see some output scroll by: | You should see some output scroll by: | ||
Line 167: | Line 171: | ||
==== The FtoC declarations ==== | ==== The FtoC declarations ==== | ||
- | |||
Next we have this code: | Next we have this code: | ||
Line 189: | Line 192: | ||
That right there is one of the biggest selling points of Ada: The ability to create your own types, with your own constraints. It might not seem like a big deal but, believe me, it is. In this case we've created a new [[http://www.adaic.com/standards/05rm/html/RM-3-2-2.html|subtype]] of the built-in subtype [[http://www.adaic.com/standards/05rm/html/RM-3-5-4.html|Natural]]. We've constrained the type's range to //0 .. 212//, meaning that objects declared as a //Fahrenheit_Degree_Range// can **never** go below 0 or above 212. If a value outside this range is assigned to an object of the //Fahrenheit_Degree_Range// type, a //Constraint_Error// exception is raised. | That right there is one of the biggest selling points of Ada: The ability to create your own types, with your own constraints. It might not seem like a big deal but, believe me, it is. In this case we've created a new [[http://www.adaic.com/standards/05rm/html/RM-3-2-2.html|subtype]] of the built-in subtype [[http://www.adaic.com/standards/05rm/html/RM-3-5-4.html|Natural]]. We've constrained the type's range to //0 .. 212//, meaning that objects declared as a //Fahrenheit_Degree_Range// can **never** go below 0 or above 212. If a value outside this range is assigned to an object of the //Fahrenheit_Degree_Range// type, a //Constraint_Error// exception is raised. | ||
- | With the //Fahrenheit_Degree_Range// type in place, we direct our attention to the declaration and assignment of the //Fahr// variable. If you've never seen this syntax before or it makes no sense to you, please read the [[Variables|and Constants]] article on this Wiki, and then return here. | + | With the //Fahrenheit_Degree_Range// type in place, we direct our attention to the declaration and assignment of the //Fahr// variable. If you've never seen this syntax before or it makes no sense to you, please read the [[Variables and Constants]] article on this Wiki, and then return here. |
The //Fahr// variable is of the //Fahrenheit_Degree_Range// type and it's initial value is 0. Why 0? Because we assign it the value //Fahrenheit_Degree_Range'First//, and the //'First// part equals the first value in the range constraint of the type, in this case 0. Consequently the value for //Fahrenheit_Degree_Range'Last// is 212. | The //Fahr// variable is of the //Fahrenheit_Degree_Range// type and it's initial value is 0. Why 0? Because we assign it the value //Fahrenheit_Degree_Range'First//, and the //'First// part equals the first value in the range constraint of the type, in this case 0. Consequently the value for //Fahrenheit_Degree_Range'Last// is 212. | ||
- | Lets take a look at the final three object declarations: | + | Let's take a look at the final three object declarations: |
- | <source lang="ada"> | + | <code ada> |
Factor : constant := 5.0 / 9.0; | Factor : constant := 5.0 / 9.0; | ||
Offset : constant := 32; | Offset : constant := 32; | ||
Step : constant := 1; | Step : constant := 1; | ||
- | </source> | + | </code> |
- | What's going on here? Why are there no //type// associated with any of those declarations? Well, if you've read the [[Variables|and Constants]] article, you will know that these constants are called [[http://www.adaic.com/standards/05rm/html/RM-3-3-2.html|named numbers]] and their //type// is called an [[http://www.adaic.com/standards/05rm/html/RM-3-4-1.html|universal type]]. Named numbers can be of any size and precision. | + | What's going on here? Why are there no //type// associated with any of those declarations? Well, if you've read the [[Variables and Constants]] article, you will know that these constants are called [[http://www.adaic.com/standards/05rm/html/RM-3-3-2.html|named numbers]] and their //type// is called an [[http://www.adaic.com/standards/05rm/html/RM-3-4-1.html|universal type]]. Named numbers can be of any size and precision. |
The //Factor// and //Offset// constants are used in the Fahrenheit-to-Celsius calculation, and //Step// define how many conversions we do in the program. | The //Factor// and //Offset// constants are used in the Fahrenheit-to-Celsius calculation, and //Step// define how many conversions we do in the program. | ||
==== The FtoC body ==== | ==== The FtoC body ==== | ||
- | |||
With the declarations out of the way, we turn our attention to the body of the program: | With the declarations out of the way, we turn our attention to the body of the program: | ||
- | <source lang="ada"> | + | <code ada> |
begin | begin | ||
loop | loop | ||
Line 222: | Line 224: | ||
end loop; | end loop; | ||
end FtoC; | end FtoC; | ||
- | </source> | + | </code> |
The reserved word //begin// signifies the beginning of the //body// and that same body ends with the final //end FtoC//. Between those two, we have a bunch of statements. | The reserved word //begin// signifies the beginning of the //body// and that same body ends with the final //end FtoC//. Between those two, we have a bunch of statements. | ||
==== The loop ==== | ==== The loop ==== | ||
- | |||
The first one is the //loop// statement. Loops come in many different shapes in Ada, each of which obeys the basic premise of | The first one is the //loop// statement. Loops come in many different shapes in Ada, each of which obeys the basic premise of | ||
- | <source lang="ada"> | + | <code ada> |
loop | loop | ||
- | some statements | + | -- some statements |
end loop; | end loop; | ||
- | </source> | + | </code> |
Loops can be terminated using the //exit// keyword: | Loops can be terminated using the //exit// keyword: | ||
- | <source lang="ada"> | + | <code ada> |
loop | loop | ||
- | some statements | + | -- some statements |
if X = Y then | if X = Y then | ||
exit; | exit; | ||
end if; | end if; | ||
end loop; | end loop; | ||
- | </source> | + | </code> |
This construction is so common that a shorter version has been made available: | This construction is so common that a shorter version has been made available: | ||
- | <source lang="ada"> | + | <code ada> |
loop | loop | ||
- | some statements | + | -- some statements |
exit when X = Y; | exit when X = Y; | ||
end loop; | end loop; | ||
- | </source> | + | </code> |
It is this last version we use in the **FtoC** program and we exit the loop when //Fahr// equals the //'Last// value of the //Fahrenheit_Degree_Range// type. | It is this last version we use in the **FtoC** program and we exit the loop when //Fahr// equals the //'Last// value of the //Fahrenheit_Degree_Range// type. | ||
==== FtoC output - putting integers on the screen ==== | ==== FtoC output - putting integers on the screen ==== | ||
- | |||
Immediately after the //loop// statement we encounter the //Put// procedure: | Immediately after the //loop// statement we encounter the //Put// procedure: | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Fahr, Width => Fahrenheit_Degree_Range'Width); | Put (Item => Fahr, Width => Fahrenheit_Degree_Range'Width); | ||
- | </source> | + | </code> |
We know from the declaration that //Fahr// is a subtype of //Natural//, which in turn is a subtype of //Integer//, so the call to //Put// on this line actually calls //Ada.Integer_Text_IO.Put//. As you can see, we give //Put// two parameters: //Item// and //Width//. The meaning of //Item// should be obvious: It's the integer we want to output, in our case the //Fahr// variable. | We know from the declaration that //Fahr// is a subtype of //Natural//, which in turn is a subtype of //Integer//, so the call to //Put// on this line actually calls //Ada.Integer_Text_IO.Put//. As you can see, we give //Put// two parameters: //Item// and //Width//. The meaning of //Item// should be obvious: It's the integer we want to output, in our case the //Fahr// variable. | ||
Line 272: | Line 272: | ||
The //'Width// attribute returns the maximum width of the type, so if we change the //Fahrenheit_Degree_Range// later on, we wouldn't have to do a single thing about this call to //Put//; it would simply adjust itself accordingly. | The //'Width// attribute returns the maximum width of the type, so if we change the //Fahrenheit_Degree_Range// later on, we wouldn't have to do a single thing about this call to //Put//; it would simply adjust itself accordingly. | ||
- | Lets do some tests with various //Width// parameters: | + | Let's do some tests with various //Width// parameters: |
- | <source lang="ada"> | + | <code ada> |
Put (Item => Fahr); -- Default Width parameter | Put (Item => Fahr); -- Default Width parameter | ||
- | </source> | + | </code> |
The output now looks like this: | The output now looks like this: | ||
Line 289: | Line 289: | ||
Lots of wasted space there. This is because //Put// now sets aside space for the //Integer//, which is the type //Fahrenheit_Degree_Range// is derived from. Let's try with 0: | Lots of wasted space there. This is because //Put// now sets aside space for the //Integer//, which is the type //Fahrenheit_Degree_Range// is derived from. Let's try with 0: | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Fahr, Width => 0); -- Minimum required characters for the integer | Put (Item => Fahr, Width => 0); -- Minimum required characters for the integer | ||
- | </source> | + | </code> |
And the output: | And the output: | ||
- | 0 -17.78 | + | 0 -17.78 |
- | 1 -17.22 | + | 1 -17.22 |
- | 2 -16.67 | + | 2 -16.67 |
- | 3 -16.11 | + | 3 -16.11 |
- | 9 -12.78 | + | 9 -12.78 |
- | 10 -12.22 | + | 10 -12.22 |
- | 11 -11.67 | + | 11 -11.67 |
- | 99 37.22 | + | 99 37.22 |
- | 100 37.78 | + | 100 37.78 |
- | 101 38.33 | + | 101 38.33 |
- | ... | + | ... |
Unfortunately for readability, the //Fahr// integer literal is no longer right-justified. Each number is given the exact width necessary to hold it and no more. | Unfortunately for readability, the //Fahr// integer literal is no longer right-justified. Each number is given the exact width necessary to hold it and no more. | ||
Line 311: | Line 311: | ||
Let's try it with a //Width// that is wide enough for some of the //Fahr// values, but not all of them: | Let's try it with a //Width// that is wide enough for some of the //Fahr// values, but not all of them: | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Fahr, Width => 2); -- Minimum width of 2. Expands if necessary | Put (Item => Fahr, Width => 2); -- Minimum width of 2. Expands if necessary | ||
- | </source> | + | </code> |
This outputs: | This outputs: | ||
- | 0 -17.78 | + | 0 -17.78 |
- | 1 -17.22 | + | 1 -17.22 |
- | 2 -16.67 | + | 2 -16.67 |
- | 9 -12.78 | + | 9 -12.78 |
- | 10 -12.22 | + | 10 -12.22 |
- | 11 -11.67 | + | 11 -11.67 |
- | 98 36.67 | + | 98 36.67 |
- | 99 37.22 | + | 99 37.22 |
- | 100 37.78 | + | 100 37.78 |
- | 101 38.33 | + | 101 38.33 |
- | 102 38.89 | + | 102 38.89 |
- | 103 39.44 | + | 103 39.44 |
- | ... | + | ... |
As you can see, the single digit values are right-justified and padded with 1 space, the two-digit values come out even, but the rest of the results are expanded to hold the third character. | As you can see, the single digit values are right-justified and padded with 1 space, the two-digit values come out even, but the rest of the results are expanded to hold the third character. | ||
Line 336: | Line 336: | ||
With //Put// for integer types out of the way, we move on to the next //Put//> | With //Put// for integer types out of the way, we move on to the next //Put//> | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Factor * Float (Fahr - Offset), | Put (Item => Factor * Float (Fahr - Offset), | ||
Fore => 4, | Fore => 4, | ||
Aft => 2, | Aft => 2, | ||
Exp => 0); | Exp => 0); | ||
- | </source> | + | </code> |
The //Item// parameter for this call to //Put// is a float, because //Factor// is a [[Variables_and_Constants#Named_numbers | named number]] that contains a decimal point and the expression //Fahr - Offset// is converted to a float using the //Float (Fahr - Offset)// expression. So when calling this //Put//, we're actually calling //Ada.Float_Text_IO.Put//. | The //Item// parameter for this call to //Put// is a float, because //Factor// is a [[Variables_and_Constants#Named_numbers | named number]] that contains a decimal point and the expression //Fahr - Offset// is converted to a float using the //Float (Fahr - Offset)// expression. So when calling this //Put//, we're actually calling //Ada.Float_Text_IO.Put//. | ||
Line 347: | Line 347: | ||
The //Fore// parameter gives the minimum character count necessary to output the value preceding the decimal point. As with //Width// for the integer types, //Fore// will automatically expand if necessary. //Aft// sets the precision after the decimal point, in this case 2. And finally //Exp// sets the exponent field size. A value of //Exp => 0// signifies that no exponent will be output. Anything other than zero will output the exponent symbol "E", a +/-, and the digit(s) of the exponent. Note: The value of //Exp// should not be less than zero! | The //Fore// parameter gives the minimum character count necessary to output the value preceding the decimal point. As with //Width// for the integer types, //Fore// will automatically expand if necessary. //Aft// sets the precision after the decimal point, in this case 2. And finally //Exp// sets the exponent field size. A value of //Exp => 0// signifies that no exponent will be output. Anything other than zero will output the exponent symbol "E", a +/-, and the digit(s) of the exponent. Note: The value of //Exp// should not be less than zero! | ||
- | Lets try a few different combinations: | + | Let's try a few different combinations: |
- | <source lang="ada"> | + | <code ada> |
Put (Item => Factor * Float (Fahr - Offset), | Put (Item => Factor * Float (Fahr - Offset), | ||
Fore => 10, | Fore => 10, | ||
Aft => 4, | Aft => 4, | ||
Exp => 0); | Exp => 0); | ||
- | </source> | + | </code> |
The output: | The output: | ||
Line 374: | Line 374: | ||
Or how about this: | Or how about this: | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Factor * Float (Fahr - Offset), | Put (Item => Factor * Float (Fahr - Offset), | ||
Fore => 4, | Fore => 4, | ||
Aft => 2, | Aft => 2, | ||
Exp => 1); | Exp => 1); | ||
- | </source> | + | </code> |
And the output: | And the output: | ||
Line 400: | Line 400: | ||
And finally: | And finally: | ||
- | <source lang="ada"> | + | <code ada> |
Put (Item => Factor * Float (Fahr - Offset), | Put (Item => Factor * Float (Fahr - Offset), | ||
Fore => 0, | Fore => 0, | ||
Aft => 1, | Aft => 1, | ||
Exp => 0); | Exp => 0); | ||
- | </source> | + | </code> |
This outputs: | This outputs: | ||
- | 0-17.8 | + | 0-17.8 |
- | 8-13.3 | + | 8-13.3 |
- | 9-12.8 | + | 9-12.8 |
- | 10-12.2 | + | 10-12.2 |
- | 11-11.7 | + | 11-11.7 |
- | 31-0.6 | + | 31-0.6 |
- | 320.0 | + | 320.0 |
- | 499.4 | + | 499.4 |
- | 9836.7 | + | 9836.7 |
- | 9937.2 | + | 9937.2 |
- | 10037.8 | + | 10037.8 |
- | 10138.3 | + | 10138.3 |
- | 10238.9 | + | 10238.9 |
- | ... | + | ... |
Which obviously isn't very pretty to look at. | Which obviously isn't very pretty to look at. | ||
Line 429: | Line 429: | ||
The last four lines of the **FtoC** program finish our formatting, get us out of here if we are done, and, if not, set the next value of //Fahr// to be converted: | The last four lines of the **FtoC** program finish our formatting, get us out of here if we are done, and, if not, set the next value of //Fahr// to be converted: | ||
- | <source lang="ada"> | + | <code ada> |
New_Line; | New_Line; | ||
exit when Fahr = Fahrenheit_Degree_Range'Last; | exit when Fahr = Fahrenheit_Degree_Range'Last; | ||
Fahr := Fahr + Step; | Fahr := Fahr + Step; | ||
end FtoC; | end FtoC; | ||
- | </source> | + | </code> |
The single call to //New_Line// is the reason we have to make //Ada.Text_IO// available to the **FtoC** program using //with Ada.Text_IO//. What //New_Line// does is output a single line feed. Running the program without this call to //New_Line// would result in output looking like this: | The single call to //New_Line// is the reason we have to make //Ada.Text_IO// available to the **FtoC** program using //with Ada.Text_IO//. What //New_Line// does is output a single line feed. Running the program without this call to //New_Line// would result in output looking like this: | ||
Line 442: | Line 442: | ||
The //New_Line// procedure accepts a //Spacing// parameter, meaning you can do this to output consecutive line feeds: | The //New_Line// procedure accepts a //Spacing// parameter, meaning you can do this to output consecutive line feeds: | ||
- | <source lang="ada"> | + | <code ada> |
New_Line (Spacing => 5); | New_Line (Spacing => 5); | ||
- | </source> | + | </code> |
Or simply | Or simply | ||
- | <source lang="ada"> | + | <code ada> |
New_Line (5); | New_Line (5); | ||
- | </source> | + | </code> |
- | We've already discussed the //exit when// method of [[FtoC_-_A_Fahrenheit_to_Celsius_converter#The_loop | terminating a loop]], and the final statement is merely a simple counter. The value of //Fahr// is incremented with //Step// on each iteration of the loop. When //Fahr// equals //Fahrenheit_Degree_Range'Last//, the loop is terminated. | + | We've already discussed the //exit when// method of [[FtoC_-_A_Fahrenheit_to_Celsius_converter#The_loop|terminating a loop]], and the final statement is merely a simple counter. The value of //Fahr// is incremented with //Step// on each iteration of the loop. When //Fahr// equals //Fahrenheit_Degree_Range'Last//, the loop is terminated. |
The final line, //end Ftoc;//, signifies the end of the program. There's nothing more to do and nothing more to see. Control is handed back to whatever called the program in the first place, and life goes on. | The final line, //end Ftoc;//, signifies the end of the program. There's nothing more to do and nothing more to see. Control is handed back to whatever called the program in the first place, and life goes on. |