LAST PAGE  BACK TO INDEX  NEXT PAGE

[20.0] VEE Notes

[20.0] VEE Notes

v5.0 / 20 of 23 / 01 sep 99 / gvg

* This chapter collects a number of interesting topics that weren't easily assembled elsewhere.


[20.1] FLOATING-POINT ISSUES
[20.2] RUNNING OUT OF MEMORY ON VEE-UX
[20.3] CONVERTING STRINGS TO ENUMS
[20.4] VEE & TIMEZONE VARIABLE
[20.5] NIL INPUTS ON VEE
[20.6] TIMER OBJECT SPAN IN VEE
[20.7] AN IF-THEN-ELSE PUZZLE
[20.8] SEEDING THE RANDOM-NUMBER GENERATOR
[20.9] USING THE FUNCTION GENERATOR VIRTUAL INSTRUMENT
[20.10] ENTERING NON-ASCII CHARACTERS UNDER VEE
[20.11] A VEEWIN HIDDEN FEATURE

 BACK TO INDEX

[20.1] FLOATING-POINT ISSUES

* Users often call us with what they perceive as numeric errors in VEE; for example, one set a a REAL constant to the value 8.9972, and when he ran it through a To Printer object, the result was 8.997199999999999. He was certain this was a bug, that VEE had a roundoff error.

I replied that this was expected behavior precisely because there was no roundoff error. What he was seeing was the full precision of the number as VEE understood it. It wasn't exactly 8.9972 because VEE uses binary floating-point numbers to represent real numbers, and these are an approximation to an actual real for two reasons:

The reason it was being printed in this full-precision / no roundoff format is because the default format in most transaction objects is:


   WRITE TEXT A EOL
 
-- which implies "standard format": a format that will accept any input, text or numeric, and pass it out as best it can.

In a sense, however, there was a bug, since the standard format actually output the number to about a digit more than its maximum reliable precision, which meant that VEE would very often display numbers in the unsettling format shown above. In current versions of VEE, the standard format was modified to output the number to about a digit less than its standard precision, greatly reducing reports of trouble.

Even if you have an older version of VEE, if you want to print out a specific precision -- say, 6 digits -- you can set the transaction to:


   WRITE TEXT A REAL STD:6 EOL
 
-- or whatever precision you like. The same goes for To File objects or AlphaNumeric objects. If you don't like the way the number is stored or displayed, you can readily change it.

The same user also said that when he increased the precision of the Real Constant, he could not input 8.9972 without it being extended to 8.997199 ... In this case, VEE was simply doing what it was told to do. Not a problem, since the precision could be set to whatever the user liked.

Similarly, a European field person posed an interesting and parallel problem to me concerning errors with modulo-divide ("remainder" operation) and integer-divide operations on VEE and other platforms. He performed the following computations:


   ________________________________________________________

                   VEE-PC  IBASIC  VE-/UX  WINCALC  CORRECT
   ________________________________________________________

   10 MOD 0.1      0.1     0       0.1     0.1      0
   10 MOD 0.2      0.2     0       0.2     0.2      0
   10 MOD 0.25     0       0       0       0        0
   9.6 MOD 3.2     3.2     3.2     3.2     3.2      0
   9.6 DIV 3.2     2       2       2       2        3
   ________________________________________________________
 
The results seemed bogus but after looking at it for a while I concluded, once more, that it was due to the fact that binary fractions are usually only approximations of decimal ones. 0.1 is not exactly 0.1 and the division algorithm is not 100% precise either, so it doesn't quite divide evenly into 10 and leaves a remainder almost exactly (but not quite) 0.1. Do a MOD of 100 by 10 and it works fine. It's only the fractional representation that gives you a hard time.

The fact that the error propagates across platforms is also understandable (though the IBASIC versus VEE PC discrepancy is odd): the error occurs down to the math library or floating-point processor level.

Such floating-point problems are universal and occur in all languages that use binary floating-point math, you get similar results in C. It is, however, much more visible in VEE than in C, because in VEE we get to see it happen, while in C we have to print things out to see them -- and so we often get complaints of this nature, and it proves difficult to explain that VEE is operating correctly.

 TOP OF PAGE

[20.2] RUNNING OUT OF MEMORY ON VEE-UX

* When we have customers call us to complain they are getting an "out-of-memory" error on VEE for HP-UX, when they actually have plenty of memory. We always tell them to modify the "maxdsiz" (maximum data segment size) in Kernel Configuration -> Configurable Parameters to tell the HP-UX to give VEE more room to move.

This often leads to a protest that the program isn't using that much data, so something must be wrong. Actually, since HP-UX doesn't execute the VEE program directly (it's executed by VEE). The entire VEE program is seen as "data", not just the variables and arrays.

 TOP OF PAGE

[20.3] CONVERTING STRINGS TO ENUMS

* A VEE user wanted to know if there were some way to convert an arbitrary string of items:


   "X1","X2","X3","X4","X5"
 
-- into an Enum list. As it turns out, there is a way: convert it to a string array and feed it into the Enum Values pin of an Enum object. For example:

   +--------------------------+   +--------------------------+
   |           Text           |   |       From String        |
   +--------------------------+   +--------------------------+
   | "X1","X2","X3","X4","X5" +-->| READ TEXT x QSTR ARRAY:* +-+
   +--------------------------+   +-------------+------------+ |
                                                |              |
                                    +-----------|--------------+
                                    |           |
                                    |   +-------+-------+
                                    |   | Radio Buttons |
                                    |   +---------------+
                                    |   | <*> X1        |
                                    |   | < > X2        |
                                    +-->| < > X3        |
                            Enum Values | < > X4        |
                                        | < > X5        |
                                        +---------------+ 
 
Note that the From String is set to QUOTED STRING format, and that array length is given as "*" (TO END). The Radio Button object does not have to be set to any particular number of Enum entries. It will adjust automatically.

 TOP OF PAGE

[20.4] VEE & TIMEZONE VARIABLE

* You can set a TZ (timezone) environment variable in your PC. A user in Japan set the timezone variable:


   SET TZ=JST
 
-- and then found that VEE's "now()" function returned the time moved forward 9 hours. He was forced to use the following workaround:

   now() - 9*3600
 
As it turned out, this wasn't a bug. VEE assumed that the system clock was set to GMT and if the TZ variable was set, generated the proper offset from GMT. This was 9 hours for Nippon. Since the PC's clock was set to local time, not GMT, the TZ put it 9 hours off.

This is consistent with HP-UX usage. The problem is that different PC applications don't interpret the TZ variable in the same way.

 TOP OF PAGE

[20.5] NIL INPUTS ON VEE

* A user wanted to know how to build a function to test for NIL inputs on VEE. This function would return "0" if the input was NIL and "1" if it wasn't.

I thought this was a simple question and came up with the following program:


                +---------------+
                | Call Function |
   +-------+    +---------------+   +------+   
   |  OK   +--->|    NilTest    +-->| 0    |
   +-------+    +---------------+   +------+
 
-- where OK button generates a NIL container, and the UserFunction NilTest gives:

   +----------------------------------------+
   |                 NilTest                |
   +---+--------------------------------+---+
   |   |                                |   |
   |   |   +------------+               |   |
   | A +-->| Get Values +--+ totsize    |   |
   |   |   +------------+  |  pin       |   |
   |   |                   |            |   |
   |   |   +---------------+            |   |
   |   |   |                            |   |
   |   |   |   +-------------------+    |   |
   |   |   +-->| ( A==0 : 0 ? 1 )  +--->| X |
   |   |       +-------------------+    |   |
   |   |                                |   |
   +---+--------------------------------+---+
 
Note that you have to use array "Get Values" and not the "totsize()" function, since "Get Values" returns "0" for a NIL container, while "totsize()" returns "1", a distinction which has implications.

Anyway, I passed this back to the user feeling pleased with myself and then he replied that the UserFunction didn't work in a different context:


                +--------------+
                |   Formula    |
   +-------+    +--------------+   +------+   
   |  OK   +--->|  NilTest(A)  +-->| 1    |
   +-------+    +--------------+   +------+
 
I finally figured out what he was getting at and then wondered: what does a Formula box do if it gets a NIL input? I revised my program to test:

                +---------+    +---------------+
                | Formula |    | Call Function |
   +-------+    +---------+    +---------------+   +------+   
   |  OK   +--->|    A    +--->|    NilTest    +-->| 1    |
   +-------+    +---------+    +---------------+   +------+
 
As it turned out, the Formula box (which in this case simply wired the input to the output) converted the NIL to "0". I checked with the lab and this is specified behavior for the Formula box. There are pros and cons to this usage, but as changing it would cause problems for people with existing programs, there are no plans to change it.

This is also why "totsize()" returns "1" for a NIL input: the numeric functions are basically specialized cases of the Formula box, and so inherit its behavior.

 TOP OF PAGE

[20.6] TIMER OBJECT SPAN IN VEE

* We had complaints that on certain platforms, the VEE Timer object could not handle a time span greater than 4500 seconds, it would roll over to 0.

According to the lab people, the maximum timer span is limited by the use of a long integer to store the span AND the rate of the fundamental hardware timing used. This means that the time span may be relatively short and it can vary from platform to platform.

So for long-period timing the appropriate technique is as follows:


     +-------+   +-------------------+
     | now() +-->| Set Variable:  t0 |
     +---+---+   +-------------------+
	 |
   +-----+-----+
   | block to  |
   | be timed  |
   +-----+-----+
	 |
  +------+------+
  | now() - t0  +-->
  +-------------+
 
This has poorer resolution but if you are counting hours, then a second's variation is not much of a problem.

 TOP OF PAGE

[20.7] AN IF-THEN-ELSE PUZZLE

* We got a very interesting question from one of our field people concerning the operation of the VEE If/Then/Else object. He had configured the conditional clause to a triadic operator:


      +-------------------------------+
      |          If/Then/Else         |
      +---+--------------------+------+
      |   |                    | then +-->
   -->| a | [ a=="x" ? 5 : 2 ] |      |
      |   |                    | else +-->
      +---+--------------------+------+
 
What does this do? If you examine the documentation for the If/Then/Else object, it says that if the conditional clause returns a nonzero value, it will return that value out the "then" pin. If it returns a zero value, it pops a zero out the "else" pin. This means that whether the triadic operator succeeds (generating a "5") or fails (generating a "2"), the appropriate value ("5" or "2") goes out the "then" pin.

Now just to make things a little more devious:


      +-------------------------------+
      |          If/Then/Else         |
      +---+--------------------+------+
      |   |                    | then +-->
   -->| a | [ a=="x" ? 0 : 8 ] |      |
      |   |                    | else +-->
      +---+--------------------+------+
 
This generates results which might seem counterintuitive, but logically follow the rules of the If/Then/Else object: if the triadic comparison succeeds, it generates a "0" -- which the If/Then/Else object interprets as a failure, and so generates a "0" on the "else" pin. If the triadic comparison fails, it generates an "8" -- which the If/Then/Else object interprets as a success, and so fires it out the "then" pin.

I had a nice session in the lab seeing which engineers could figure this one out (Sue, who designed the If/Then/Else object, didn't have a problem). Why the field person wanted to do this (likely he was just tinkering) we don't know, but it certainly was an amusing puzzle to figure out!

 TOP OF PAGE

[20.8] SEEDING THE RANDOM-NUMBER GENERATOR

* While this particular trick is used in examples all through this document, it's worthwhile giving it a section of its own so you can find it: to seed the VEE random-number generator with a value that allows the random values to vary greatly between runs, use a Formula object with the following contents:


   randomseed((10^9)*fracPart(now()/100))
 
This gets the subsecond value of the current time and magnifies it to a large value. This is implemented as a UserFunction named "rndseed()" in the xgenfunc.vee User Library.

 TOP OF PAGE

[20.9] USING THE FUNCTION GENERATOR VIRTUAL INSTRUMENT

* A user called me concerning the VEE Function Generator (FGen for short) object, wondering how it could be used to generate amplitude- or frequency-modulated waveforms. "I can do this with a real function generator," he said. "Why can't I do this with the virtual instrument?"

I didn't have a ready answer, but the more I thought about it the more interesting the question became.

* A real function generator in general produces a continuous analog output waveform in real time. The FGen, in contrast, produces a so-called "waveform" object that contains numeric information defining the time duration of the waveform and data points defining the amplitude of that waveform during that time span.

In short, the virtual instrument is make-believe. It can generate data that can simulate that acquired by a scope or other type of digitizer from a real function generator, or provide data (with a little processing) to, say, an arbitrary-function generator that could then actually generate the real data.

The FGen has the appearance:


   +--------------------------+
   |    Function Generator    |
   +--------------------------+
   | Function   [ Cosine ][v] |
   | Frequency  [    1000   ] |
   | Amplitude  [     1     ] |
   | DcOffset   [     0     ] |
   | Phase      [Deg][v][ 0 ] |
   | Time Span  [    20m    ] |
   | Num Points [    256    ] |
   +--------------------------+
 
All the settings available on the panel view can be brought out as input pins to allow them to be controlled programmably.

When this object is fired, it generates a waveform data object, which consists (given the settings above) of an array of 256 real numbers plus the 20-millisecond timespan. You can display this data set or put it in a file or whatever.

Suppose you want to amplitude-modulate an FGen with output from a lower-frequency one. This is very easy to do, all you have to do is multiply the two waveforms:


   +--------------------------+
   |    Function Generator    |
   +--------------------------+
   | Function   [ Cosine ][v] |
   | Frequency  [    200    ] |
   | Amplitude  [     1     ] |
   | DcOffset   [     0     ] +---+
   | Phase      [Deg][v][ 0 ] |   |
   | Time Span  [    20m    ] |   |
   | Num Points [    256    ] |   |
   +--------------------------+   +---->+-------+
                                        | a * b +-->
   +--------------------------+   +---->+-------+
   |    Function Generator    |   |
   +--------------------------+   |
   | Function   [ Cosine ][v] |   |
   | Frequency  [    5000   ] |   |
   | Amplitude  [     1     ] |   |
   | DcOffset   [     0     ] +---+
   | Phase      [Deg][v][ 0 ] |
   | Time Span  [    20m    ] |
   | Num Points [    256    ] |
   +--------------------------+
 
Multiplying two waveforms in VEE simply results in each element of one waveform being multiplied by the corresponding element of another. Note use of the word "corresponding": this is only going to work if the two waveforms have the same timespan and number of points, otherwise you'll get an error message (which is just as well, since the operation then doesn't make any sense).

Well, OK, then, how about frequency modulation? That might seem easy to do; just bring out a function pin to set frequency on one FGen and then feed it the output of a modulating function generator:


   +------+    +---+------+
   | Fgen +--->| F | FGen |
   +------+    +---+------+
 
However, this won't work. You'll get back an error message saying that the frequency input demands a scalar. This means that the way the FGen is defined, it can only generate a waveform data object of a fixed frequency, which is (as far as I am concerned) also just as well, since making it more flexible than that would be complicated and the demand for doing it isn't there.

So what about breaking the modulating waveform into its scalar elements and then simply generating a set of waveform data objects that are finally added up? Let's try this as a brute-force approach:


       +------+
       | Fgen +--+
       +------+  |
                 |
  +--------------+
  |
  |   +------------------------+
  |   |    UnBuild Waveform    | 
  |   +---------+-+------------+
  +-->| Wf Data | |  Real Ary  +---+
      |         | | Time Span  |   |
      +---------+-+------------+   |
                                   |
  +--------------------------------+
  |
  |   +------------+   +-----------+
  +-->| totSize(x) +-->| For Count +--+
  |   +------------+   +-----------+  |
  |                                   |
  |   +-------------------------------+
  |   |
  |   |   +-----------------+
  |   |   |     Formula     |
  |   |   +---+---------+---+   +---+------+
  |   +-->| I |[ F[I]  ]| R +-->| F | FGen +----->+--------------+
  +------>| F |         |   |   +---+------+      | Concatenator +--+-->
          +---+---------+---+                 +-->+--------------+  |
                                              |                     |
                                              +---------------------+
 
This works, sort of. It produces a set of waveform objects of the desired frequency and links them together. However, the results are pretty useless. This is because each of elements has a time duration unrelated to the actual modulating frequency's rate of change.

To add this item, you need to add more computations to the program: get the number of points in the original waveform, determine the time interval between each data point, and feed that into the final FGen to provide the timespan for each output object.

This will generate the proper time-intervals, with one last problem: you end up with discontinuities between each of the waveforms since they all start at the same place. I can't think of a way around that.

In short, the results of trying to do frequency modulation through two ganged function generators are hardly worth the bother. It would probably provide better results and work much more effectively to compute the waveform with basic math functions.

 TOP OF PAGE

[20.10] ENTERING NON-ASCII CHARACTERS UNDER VEE

* A user wanted to know how to type non-ASCII characters into a VEE Text Constant. This can be done easily enough on a Windows platform: you can type the decimal ascii code (on the numeric keypad only) while holding down the ALT key, and voila, the corresponding character is entered.

For example, Alt-132 gives you a lowercase "a" with an umlaut. Unfortunately, this trick doesn't work on an HP-UX system.

 TOP OF PAGE

[20.11] A VEEWIN HIDDEN FEATURE

* There is a special feature in VEE for Windows which is interesting but undocumented. To get to this feature, all you have to do is bring up the "Help -> About" panel and click on it with your secondary mouse button three times, rather slowly and pausing slightly between mouse clicks. Try varying the click rate until you get it right.

 TOP OF PAGE


 LAST PAGE  BACK TO INDEX  NEXT PAGE