Code: Select all
ushortNum := 64759
shortNum := ushortNum >> 15 ? -((ushortNum ^ 0xFFFF) + 1) : ushortNum
MsgBox shortNum
Code: Select all
ushortNum := 64759
shortNum := ushortNum >> 15 ? -((ushortNum ^ 0xFFFF) + 1) : ushortNum
MsgBox shortNum
To be clear, I never criticized your method of using NumPut and NumGet to extract a number of one type from a value of another type. I criticized:
Why do you think it is a good idea to tell me what I did or wrote, especially after you've already been contradicted several times in this topic? All you've achieved is to show how little you understand it.In any case, you proved yourself that the exact method the C/C++ code uses to sign extend isn't based off the size of an integer buffer (apparently hard coded to 8 bytes in AHK) but rather it calls the sign extend assembly instruction via type casts.
Code: Select all
void NumGetShort(__int64 *a, UINT_PTR b) {
*a = *(short*)b;
}
void NumGetInt64(__int64 *a, UINT_PTR b) {
*a = *(__int64*)b;
}
Code: Select all
?NumGetInt64@@YAXPEA_J_K@Z PROC ; NumGetInt64, COMDAT
00000 48 8b 02 mov rax, QWORD PTR [rdx]
00003 48 89 01 mov QWORD PTR [rcx], rax
00006 c3 ret 0
?NumGetInt64@@YAXPEA_J_K@Z ENDP
?NumGetShort@@YAXPEA_J_K@Z PROC ; NumGetShort, COMDAT
00000 48 0f bf 02 movsx rax, WORD PTR [rdx]
00004 48 89 01 mov QWORD PTR [rcx], rax
00007 c3 ret 0
?NumGetShort@@YAXPEA_J_K@Z ENDP
Even at the assembly level, behaviour is defined by the data type, not "the size of the underlying buffer", which is also defined by the data type. It is well known and documented that AutoHotkey uses signed 64-bit integers.I tend to avoid bit-magic that relies on the size of the underlying buffer,
It's even nice to be back at school again. Thank you!lexikos wrote: ↑28 Mar 2024, 22:17Understanding any of these methods probably requires an understanding of two's complement.
iseahound wrote: ↑29 Mar 2024, 21:51No one needs to agree with my opinion. They're just simple facts:
For 1, see https://graphics.stanford.edu/~seander/bithacks.html (really good and classic source for bit hacks)
- Bit magic is pretty much solved so it doesn't really represent a good body of reusable knowledge, but rather a collection of curios.
- Numbers seem to pop up out of nowhere.
For 2 just look up De Bruijn numbers
Code: Select all
// C++
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define MAKELPARAM(l, h) ((LPARAM)(DWORD)MAKELONG(l, h))
#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
Code: Select all
; AHK
GET_X_LPARAM(lParam) => (lParam << 48 >> 48) ; Int == Short
LOWORD(l) => (l << 48 >>> 48) ; WORD == UShort
GET_Y_LPARAM(lParam) => (lParam << 32 >> 48) ; Int == Short
HIWORD(l) => (l << 32 >>> 48) ; WORD == UShort
MAKELPARAM(l, h) => (lp := (l & 0xffff) | ((h & 0xffff) << 16), A_PtrSize == 8 ? lp : (lp << 32 >> 32)) ; LPARAM == Ptr
MAKELONG(a, b) => (((a & 0xffff) | ((b & 0xffff) << 16)) << 32 >> 32) ; LONG == Int
Integer constants and numeric strings outside of the supported range (of 64-bit signed integers) now overflow/wrap around, instead of being capped at the min/max value. This is consistent with math operators, so 9223372036854775807+1 == 9223372036854775808 (but both produce -9223372036854775808). This facilitates bitwise operations on 64-bit values.
but couldn't really find clear documentation that the integers are 64-bit.The ~ (bitwise-NOT) operator now always treats its input as a 64-bit signed integer; it no longer treats values between 0 and 4294967295 as unsigned 32-bit.
I'm not sure I even understand what you mean by "organized in memory". If you mean how the negative number relates to the exact sequence of bits (1s and 0s), I think the lack of symmetry in your formula obscures even that. I didn't fully understand it until I considered that unary negation literally finds the two's complement of its operand, and that's exactly what ((ushortNum ^ 0xFFFF) + 1) does (but for 16-bit).teadrinker wrote: ↑29 Mar 2024, 11:54[The variant] I gave also gives you a better idea of how negative numbers are organized in memory.
Code: Select all
shortNum := ushortNum >> 15 ? (((ushortNum ^ 0xFFFF) + 1) ^ 0xFFFFFFFFFFFFFFFF) + 1 : ushortNum
That's your opinion, actually.
Are you ignoring the other "bit magic" methods that were demonstrated, which do not rely on the position of the sign bit?
Code: Select all
MsgBox position_of_sign_bit()
position_of_sign_bit() {
Loop
if (1 << A_Index) < 0
return A_Index
}
Code: Select all
sign_bit := (1 << 31) < 0 ? 31 : 63
MsgBox sign_bit
I think it is safe to assume that a group largely composed of people new to programming would have "many" members who have not even learned what "64-bit" means. So what you say is probably true, though I think "many readers of this forum" is misdirection and not something you actually know. There is no shame in saying "I didn't know that".iseahound wrote: Many readers of this forum did not know that AHK uses 64-bit integers on both 32- and 64-bit versions (including myself).
Can you prove that the same argument does not apply equally to your own method of using NumPut and NumGet?iseahound wrote:It only takes one yet-to-be-discovered counterexample to prove that these bitwise manipulations cannot be trusted
This technique was already demonstrated in the documentation, and therefore can be assumed safe to use. It is common sense that one does not need to understand the underlying mechanics of the code to put it to use reliably. It is certainly appropriate to put documented examples to use.iseahound wrote:Since you have to know the position of the sign bit (or know the size of the integer data type) to fully realize bitwise operations, and this is not common knowledge, it wasn't appropriate (to do things like <<32>>32) until lexikos gave his reply in this thread.
It even says "AutoHotkey natively uses signed 64-bit integers", though the example can be trusted to work regardless.If an incoming parameter is intended to be a signed integer, any negative numbers can be revealed by following either of the following methods:Source: CallbackCreate - Syntax & Usage | AutoHotkey v2Code: Select all
; Method #1 if (wParam > 0x7FFFFFFF) wParam := -(~wParam) - 1 ; Method #2: Relies on the fact that AutoHotkey natively uses signed 64-bit integers. wParam := wParam << 32 >> 32
In the registry, REG_DWORD values are always expressed as positive decimal numbers. If the number was intended to be negative, convert it to a signed 32-bit integer by using OutputVar := OutputVar << 32 >> 32 or similar.
Source: RegRead - Syntax & Usage | AutoHotkey v2
Would you say that it is common knowledge that NumPut and NumGet can be used to extract the X and Y components of lParam? Would the majority of users have ever even had reason to consider doing such a thing?iseahound wrote:this is not common knowledge
What integers? Integers stored in variables, arrays, map elements? Integers used as keys in a map, indices in an array? Integers passed to various built-in functions? You want a clear answer, but "are integers 64-bit?" is the wrong question.iseahound wrote:but couldn't really find clear documentation that the integers are 64-bit.
If Value2 is less than 0 or greater than 63, an exception is thrown.
Source: Variables and Expressions - Definition & Usage | AutoHotkey v2
For example, -1 has the same bit representation as the unsigned 64-bit integer 0xffffffffffffffff, therefore -1 >>> 1 is 0x7fffffffffffffff.
Source: Variables and Expressions - Definition & Usage | AutoHotkey v2
It can be assumed, otherwise the documentation would be incorrect. If 64-bit integers were not supported on 32-bit, the unqualified statement "64-bit signed values are supported" would be false.
Users browsing this forum: Draken and 25 guests