News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

A few random and curious questions:

Started by Frizie, Aug 26, 2024, 03:46 PM

Previous topic - Next topic

Frizie

Here are a few random and curious questions:

-- 1.--
I'm looking for an easy way to calculate the power of 10.
If ( x = 1 ), then ( y = 10^1 = 10 ).
If ( x = 2 ), then ( y = 10^2 = 100 ).
If ( x = 3 ), then ( y = 10^3 = 1000 ) etc.

With function POW I can't do this (and ^ is reserved for XOR).
Now I do it with a For...Next loop:
y = 1
For z = 1 To x
  y = y * 10
Next

But can it be even simpler?
i.e. is there already a build-in function for this?


-- 2.--
This code gives errors:
Dim swMyVar1 As Sword
Dim wMyVar   As Word
If swMyVar1 > -wMyvar Then...


This code works:
Dim swMyVar1 As Sword
Dim swMyVar2 As Sword
Dim wMyVar   As Word
swMyVar2 = -wMyVar

If swMyVar1 > swMyVar2 Then...

If I put -wMyVar first in a SWord, then it works as expected.
The question is whether this is allowed, because I can't find anything about it in the manual (maybe I have missed it).
If this is not allowed, what is then the easiest way to convert a value in a variable to a negative value.


-- 3.--
I often use SUB...ENDSUB, which originated in the time when PROC...ENDPROC did not yet exist.
Are there any advantages to only using PROC...ENDPROC (without parameters) in the future or is it recommended to stick with SUB...ENDSUB in that case and only use procedures if parameters are also needed?


-- 4.--
Browsing through the manual I found the keyword SKIP for HSerin, HRsin and Rsin, but I don't see any examples because I suspected that this is what I was looking for.
How should SKIP be used?

In addition, the keyword SKIP is not bold in the editor.


Thanks in advance for answering (no rush).
Ohm sweet Ohm | www.picbasic.nl

trastikata

Hi Frizie,

#1 - if it is only for an exponent of ten fastest approach would be a look-up table.

#2 - The code looks good to me, but if you get strange results it might be related to RAM banks chnage, can you post the assembler code where for both cases where it is working and not working.

#3 - With Procs you can have locally (or Global and Shared variables) declared inside the procedure (accessible from outside too). One place where Procs cause some difficulties is the Org directive but there's a workaround.

#4 - Sorry, never used compiler comms commands.

top204

#2
What device are you running on? This is an important question because each device family has very different internal architecture and somewhat different mnemonics.

I've tried the signed comparison with a few permutations, and they work as expected.

For example:

    Device = 18F25K20                           ' Tell the compiler what device to compile for
    Declare Xtal = 16                           ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup HRSout
'
    Declare Hserial_Baud = 9600                 ' Set the Baud rate to 9600
    Declare HRSOut_Pin  = PORTC.6               ' Set the TX pin
'
' Create global variables here
'
    Dim SWordVar As SWord At $0100 = -16384
    Dim WordVar  As Word  At $0200 = 8192
     
'----------------------------------------------------------------------------
' The main program starts here
'
Main: 
    If SWordVar > -WordVar Then
        HRSOutLn SDec SWordVar, " is greater than ", SDec -WordVar
    Else
        HRSOutLn SDec SWordVar, " is less than or equal ", SDec -WordVar
    EndIf

And it displays the text: "-16384 is less than or equal -8192"

I also, deliberately, made the two variables in different RAM banks, just to make sure, and also tried the code with an enhanced 14-bit core device.

Remember, a Signed Word variable has a value range of -32768 to 32767, so if a comparison is performed with an unsigned Word variable carrying a value larger that 32767, it will actually be comparing a 2s complement negative value of the positive value, and that will not be the original value made minus, and placing a minus in front of it will create whatever the 2s complement value is of the unsigned value, so if it is carrying a value larger than 32767, it will create a negative value within it and it will not be -32767.

Also, it will always perform a signed comparison, because the variable that is comparing with something is a signed type, and signed takes precedence in comparisons and expressions. So it is always better to make the variable sign types the same in comparisons and expressions.

2s complement with a 16-bit value is the equivalent of: (65535 - Value) + 1. So for 2s complement, the value to convert to 2s complement is subtracted from the maximum value the variable can hold + 1.

The Skip modifier has a small paragraph in each coms section of the manual:

"Skip followed by a count will skip that many characters in the input stream.
For example, Skip 4 will skip 4 characters."

With "Power of", it is a sequence of multiplications, so they can overflow a variable very quickly if the values are too large, or too many powers are performed. Even with a 32-bit variable, and that is why the compiler uses Floating Point for the Pow function, so it has a bit better chance of calculating the power of because it uses Exp, but will still fail with larger values.

However, it can be done using integers only with a simple procedure, as listed below as a demo:

    Device = 18F25K20                           ' Tell the compiler what device to compile for
    Declare Xtal = 16                           ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup HRSout
'
    Declare Hserial_Baud = 9600                 ' Set the Baud rate to 9600
    Declare HRSOut_Pin  = PORTC.6               ' Set the TX pin
'
' Create global variables here
'   
    Dim Dwordin As Dword
    Dim DWordout As Dword
    
'----------------------------------------------------------------------------
' The main program starts here
'
Main: 
    Dwordin = 30
    DWordout = PowerOf(Dwordin, 3)
    HRSOutLn Dec DWordout
 
'----------------------------------------------------------------------------
' Calculate an unsigned integer Power Of
' Input     : pValue holds the value to calculate the Power Of
'           : pPow holds the Power Of value
' Output    : Returns the Powered value
' Notes     : None
'  
Proc PowerOf(pValue As Dword, pPow As Byte), Dword     
    Dim bPowLoop As Byte
    If pPow = 1 Then
        Result = pValue
        ExitProc
    EndIf
    Result = 1
    For bPowLoop = 1 To pPow 
        Result = Result * pValue 
    Next 
EndProc 

The above program listing will display the value: 27000, which is 30 to the power of 3. However, 30 to the power of 10 is actually 590490000000000, which will not fit in a 32-bit variable and will overload it and give the value: 716276736, which is incorrect because the variable was overloaded while multiplying itself with itself 10 times over, and folder over on itself. Even the compiler's Pow function will not work with such large values because it overloads a 32-bit Floating Point variable as well.

This is the problem with all compilers with certain values, even 64-bit microprocessors have problems with larger Pow parameters.

Stephen Moss

Quote from: Frizie on Aug 26, 2024, 03:46 PMIf I put -wMyVar first in a SWord, then it works as expected.
The question is whether this is allowed, because I can't find anything about it in the manual (maybe I have missed it).
If this is not allowed, what is then the easiest way to convert a value in a variable to a negative value.
Even if the compiler allows simply placing a minus character in front of a variable to convert a positive number to a negative number and vica versa it may not be the best way with reagrds to code that is readable by others, as unlike the multiply by minus one method (i.e., swMyVar2 = (wMyVar * -1)) it is a method many may be unfamilar with, leading them to think there may be a typo on your code as in...
swMyVar2 = -wMyVar should be swMyVar2 = something - wMyVar
which ia a format they are more familiar with seeing.

Frizie

#4
Thanks Trastikata and Les for the extensive answers  ;D

#1
I hadn't thought of LOOKUP.
I tried this but LOOKUPL takes up more memory space than the simple FOR...NEXT loop suggested, so I think the FOR...NEXT loop is the shortest code:
y = 1
  For z = 1 To x
  y = y * 10
Next

x in my application will be 4 at most, (y = 10, 100, 1000 or 10000),


#2
The device is 18F45K22.
I did the comparison with -Value in a laptop with V4.0.2.1, so that may be the cause of the assembler errors.
I can't get to my other laptop with the latest software version now, but I will test it again later.


#3
From now on I will ignore SUBs in new programs and do all routines (with or without parameters) with procedures.
I actually never use ORG.


#4
I assume SKIP should be specified as follows:
Hserin [Skip 4, MyVariable]

It should be noted that SKIP is not displayed as bold in the editor.

______

Stephen, I totally agree with you on the readability.
I will try your method, thanks for the tip. :D
EDIT:
Stephen, I tried your method (MyVar * -1), but it also takes 14 bytes more memory than the -MyVar method.
In addition, if you provide each line of code with good comments, misunderstandings are less likely to occur.
Ohm sweet Ohm | www.picbasic.nl

top204

#5
Remember Frizie, bigger size does not always mean slower operation. OooooErrrrrr that sounds rude. :-)

If it is a dedicated set of 'power of' values, a flash memory table will be faster than multiplications. For example the procedure in the demo below uses a flash memory table within the procedure, and each call of the iPow10 procedure takes only 6.5uS on an 18F device running only at 16MHz, but a multiplication method takes approx 90uS each call:

    Dim bPow As Byte
    Dim Wordout As Word
   
'----------------------------------------------------------------------------
' The main program starts here
'
Main:    
    For bPow = 0 To 4
        Wordout = iPow10(bPow)
        HRSOutLn Dec Wordout
    Next

'----------------------------------------------------------------------------
' Calculate an unsigned integer Power Of 10
' Input     : pPow holds the Power Of value (0 to 4)
' Output    : Returns the Powered value
' Notes     : None
'
Proc iPow10(pPow As Byte), Word
    Dim PowTable As Flash16 = 0, 10, 100, 1000, 10000
    Result = CRead16 PowTable[pPow]
EndProc

The program above will display the text below on a serial terminal:
0
10
100
1000
10000


Or you can create it as a preprocessor meta-macro for inline operation, that works more efficiently, so is faster to operate, and takes only 4uS per operation on an 18F device running at the slow speed of 16MHz:

'----------------------------------------------------------------------------
' Create a meta-macro for a fixed integer power of 10
' Input     : pPow holds the Power Of value (0 to 4)
' Output    : Returns the Powered value
' Notes     : None
'  
    Dim PowTable As Flash16 = 0, 10, 100, 1000, 10000   
$define iPow10(pPow) CRead16 PowTable[pPow]
  
'----------------------------------------------------------------------------
' The main program starts here
'
Main:    
    For bPow = 0 To 4
        Wordout = iPow10(bPow)
        HRSOutLn Dec Wordout
    Next


Frizie

Un-be-lie-va-ble!  :o
The second variant (with the meta-macro) is blazing fast but also saves 100 program bytes compared to the FOR NEXT loop!
Thanks Les ;D
I haven't done much with meta-macros yet, but I think I should look into it more.
Ohm sweet Ohm | www.picbasic.nl