Jump to content

[LE] How to return multiple values from a function?


irswat

Recommended Posts

subhuman0100 wrote: "the tone of your post seems to be that you're disagreeing with me"
I am sorry. I would not make you angry or flaming your post.

8-bit digit, from 0 .. 255
-------------------------------
hexadecimal 0x00 .. 0xff
binary 0000 0000 .. 1111 1111

10-bit digit, from 0 .. 1023
----------------------------------
hexadecimal 0x000 .. 0x3ff
binary 00 0000 0000 .. 11 1111 1111

32-bit digit unsigned, our papyrus "int" type
------------------------------------------------------
hexadecimal 0x00000000 .. 0xffffffff
decimal 0 .. 4,294,967,295

            bit31    bit23    bit15    bit7   bit0
            |        |        |        |      |
binary      00000000 00000000 00000000 00000000 ..   each bit can be unset
            11111111 11111111 11111111 11111111      or set

(1) re-order of 32 bits for better explanation

bits        10             9 8 7 6 5 4 3 2 1 0
            /             / / / / / / / / / /
            |             | | | | | | | | | |
binary      0             0 0 0 0 0 0 0 0 0 0    10 bits + unused bit10
----------------------------------------------
bits        21  20 19 18 17 16 15 14 13 12 11
            /   /  /  /  /  /  /  /  /  /  /
            |   |  |  |  |  |  |  |  |  |  |
binary      0   0  0  0  0  0  0  0  0  0  0     10 bits + unused bit21
---------------------------------------------
bits           31 30 29 28 27 26 25 24 23 22
               /  /  /  /  /  /  /  /  /  /
               |  |  |  |  |  |  |  |  |  |
binary         0  0  0  0  0  0  0  0  0  0      10 bits left

(2) code to "compress" as follow:

  int i = 0x00000000        ; init by Zero

; our three 10-bit digits, with different values as sample

  int n1 = 0x1ff            ;  511
  int n2 = 0x2ff            ;  767
  int n3 = 0x3ff            ; 1023

; store n1 into i

    i = n1                  ; that is simple

; fill bits 11 up to 20

    i = i + (n2 * 0x800)    ; hex 0x800, dec 2048

; fill bits 22 up to 31

    i = i + (n3 * 0x400000)

; explanation with binaries for variable "i"

;   00000000 00 | 0 | 00000 00000 | 0 | 00 00000000  = 0x00
;   --------------------------------------------------------------
;                                       01 11111111  = i1 = 0x1ff
;                     10111 11111                    = i2 = 0x2ff
;   11111111 11                                      = i3 = 0x3ff
;   ------------------------------------------------
;   11111111 11   0   10111 11111   0   01 11111111  = i  = 0xffd7f9ff

(3) and now the "decompress" code: Keep in mind: bit10 and bit21 are unused!
you wrote: "But I'm not sure where you're going with the modulus. Could you explain that?"

; get back the 10-bit digits

    n3 = i / 0x400000       ; div
    n1 = i % 0x800          ; modulo by 2048

;all in one

    n2 = ((i % 0x400000) - (i % 0x800)) / 0x800

; or step by step

    n2 = i % 0x400000       ; modulo by
    n2 = n2 - n1            ; subtract the first digit
    n2 = n2 / 0x800         ; div
Edited by ReDragon2013
Link to comment
Share on other sites

Another option is to use a form array. You can return multiple form types that way if you know what you're expecting. That won't work for returning say an Int and a CK form like Actor, but will work for CK forms.

My hopes and my dreams crashed and burned into eternal nothingness. Forever shall I wonder alone weeping over this sheer travesty anywhere and everywhere within nowhere. :wallbash:

Link to comment
Share on other sites

Wow, so dramatic. You gonna be alright there? If using StorageUtil from PapyrusUtil it would make this a lot easier to do:

MiscObject Property Lockpick Auto

Event OnInit() 
    Form[] MyFormArray = MyCoolFunction(Lockpick, Game.GetPlayer(), 1000)
    Int IntA = StorageUtil.GetIntValue(MyFormArray[0], "MyCoolMod_Int")
    Int IntB = StorageUtil.GetIntValue(MyFormArray[1], "MyCoolMod_Int")
EndEvent

Form[] Function MyCoolFunction(Form akForm, ObjectReference akCenter, Float akRadius)
    Form[] MyFormArray = New Form[2] 
    MyFormArray[0] = Game.FindRandomReferenceOfTypeFromRef(akForm, akCenter, akRadius)
    MyFormArray[1] = Game.FindRandomActorFromRef(akCenter, akRadius)
    
    If MyFormArray[0] 
        StorageUtil.SetIntValue(MyFormArray[0], "MyCoolMod_Int", Utility.RandomInt(0, 100))
    Endif
    
    If MyFormArray[1] 
        StorageUtil.SetIntValue(MyFormArray[1], "MyCoolMod_Int", Utility.RandomInt(0, 100))
    Endif
    
    Return MyFormArray
EndFunction

 

 

Link to comment
Share on other sites

i = i + (n2 * 0x800) ; hex 0x800, dec 2048

Yep, this I got. You're doing the same thing I did. I kinda touched on it in my earlier reply, when I said a multiplication or division by a power of 2 is a shift.

 

We, all of us "shift" dozens or hundred of times in our daily lives, but we do it without thinking about what we're doing.

If you have to figure out "89 * 100" probably nobody gets out a paper and pencil and calculates it. Any power of ten, for division or multiplication you just look at it and shift the digits over to get the answer. In this case, 100=10^2 so it's the base-10 version of a LeftShift 2, or shift the digits "89" two places to the left, and backfill with two zeroes to get "8900"

It's the same in binary, if you're multiplying or dividing by a power of two, you just shift the digits and get the answer.

So what you wrote as "n2 * 0x800" is shifting 11 bits, where I wrote it as shifting 10 bits "Math.LeftShift(n2, 10)"

They're basicly the same thing. The only question comes down to:

 

 

n2 = n2 / 0x800 ; div

That can be RightShift(n2, 11) but the difference is I don't know how well Papyrus is optimized. If you do that in GCC or Java, the compiler bytecode output will be a shift. I don't know if Papyrus will do that, and the time difference can be considerable. An x86 does well with addition, subtraction and multiplication- but comparatively, division is very slow whereas a shift is one of the most basic functions a digital device can do. It's a single clock cycle, even the room-sized early computers from 70 years ago could do a shift, the Intel 4004 from 50 years ago only had a total instruction set of 40-some instructions, and shifts were part of it. It's one of the simplest and most basic things a computer can do.

Tip: this "shifting" shortcut everyone already knows applies elsewhere, not just base-10(decimal). 0xDEADBEEF * 0x100 = DEADBEEF00

 

So you're doing what I did, but using different instructions.

 

Thank you for the modulus explanation!

I'm gonna chalk that up to "different people think differently" because what you called simpler, I see see as the opposite. But if it helps someone else, it's good!

Edited by subhuman0100
Link to comment
Share on other sites

Conclusion: Different ways lead to the same aim. I know that a division or modulo operation is slower than Shift Right or And operation.

The fact is (and that was my statement) all you do with SKSE LogicalXXX functions can be done with vanilla papyrus math operators.

 

(1) your version (is calling 11x SKSE based Math.LogicalXXX() functions to reach the aim)

 

;--------------------------------------------------
Int FUNCTION compress(Int int1, Int int2, Int int3)
;--------------------------------------------------
    int1 = Math.LogicalAnd(int1, 0x03FF)         ; ensure it\'s not more than 10 bits in length - the above mentioned check
    int2 = Math.LogicalAnd(int2, 0x03FF)         ; or else it will (in later steps) overflow into
    int3 = Math.LogicalAnd(int3, 0x03FF)         ; the next int\'s space

    int returnInt = Math.ShiftLeft(int1, 10)     ; bits 1-10 of int1 are now in position 11-20 on returnInt
    returnInt = Math.LogicalOr(returnInt, int2)  ; the previous line is still true, but now also bits 1-10 of int2 are in returnInt\'s bits 1-10

    returnInt = Math.ShiftLeft(returnInt, 10)    ; now int1 is bits 21-30 of returnInt, and int2 is bits 11-20
  ;;returnInt = Math.LogicanOr(returnInt, int3)  ; typo here!
    returnInt = Math.LogicalOr(returnInt, int3)  ; our send value is done, the previous line is still true, but we\'ve now stuffed int3 into positions 1-10
    RETURN returnInt
ENDFUNCTION

;----------------------
FUNCTION someFunction()
;----------------------
    int returnedInt = compress(0x01ff, 0x02ff, 0x03ff)

; decompress now

    int newInt3 = Math.LogicalAnd(returnedInt, 0x03FF)     ; pull out ten bits
    returnedInt = Math.ShiftRight(returnedInt, 10)         ; shift right 10 bits
    
    int newInt2 = Math.LogicalAnd(returnedInt, 0x03FF)     ; pull out next ten bits
    int newInt1 = Math.ShiftRight(returnedInt, 10)         ; shift right ten again for yes, this is my final answer

    ; voila
ENDFUNCTION

 


(2) my version (has taken vanilla papyrus math operators to get the same result) https://www.creationkit.com/index.php?title=Operator_Reference

 

  Int T = 0x400000        ; dec 4194304, defined constant for better overview

;--------------------------------------------
Int FUNCTION compress(Int n1, Int n2, Int n3)        ; n1 = int1
;--------------------------------------------
    RETURN (n1 + (n2 * 0x800) + (n3 * T))       ; 2x multiplication, 2x addition
ENDFUNCTION

;----------------------
FUNCTION someFunction()
;----------------------
    int r = compress(0x01ff, 0x02ff, 0x03ff)         ; r = returnedInt

; decompress now
    int n3 = r / T                                   ; n3 = newInt3
    int n1 = r % 0x800

; variant (1)
    int n2 = ((r % T) - (r % 0x800)) / 0x800    ; 2x division, 3x modulo, 1x substraction

; variant (2) redundant code removed
    int n2 = ((r % T) - n1) / 0x800             ; 2x division, 2x modulo, 1x substraction

    ; voila
ENDFUNCTION

 


(3) You wrote: "I'm gonna chalk that up to "different people think differently" because what you called simpler, I see see as the opposite."

Maybe you do not really understand what I meant. My suggestion was next code; no overhead, no costs to compress and decompress

  Int int1
  Int int2
  Int int3

;----------------
FUNCTION Func_1()
;----------------
    int1 = 0x01ff            ; store value here, no compression
    int2 = 0x02ff
    int3 = 0x03ff
ENDFUNCTION

;----------------
FUNCTION Func_2()
;----------------
    int i = int1            ; get back the stored value, decompression not required
    int j = int2
    int m = int3
ENDFUNCTION

That means normally its not required to get a "return of multiple values from a function".

Edited by ReDragon2013
Link to comment
Share on other sites

I was embarrassed reading this, what a pointless argument! I am sure there way more ways to return 3 lousy numbers too! lol Come on kiss and make up now, this is not that important.

 

@subhuman return 3 something NOT raw or primitive a in method that does not return a list or your argument is pointless in my humble opinion. The Collatz Conjecture needs people like you, jump to it.

Link to comment
Share on other sites

I was late the party but here's my 2 septims: Both of your routes are good there's just one difference from what I can see:

 

1. subhuman's route, the SKSE route, does all the math underneath. This is convenient for people not too savy at math. It's clean, quick, not head pounding.

2. ReDragon2013's route, the vanilla route: I like this one because he shows the math being done - this is a great way to how you learn from others. -RAMBLING START- My math teacher would always say to appreciate the work being shown with math, and for worksheets that only allowed space for answers, she wanted us to show the work on a separate sheet -RAMBLING END-. Anyway, this is also it's a proven way to get around using a 3rd party extension.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...