The documentation now explicitly states that bitwise operators perform 64-bit integer operations.
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.
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).
Your method is finding the 16-bit two's complement of ushortNum, and then finding the 64-bit two's complement of the result, much the same as this:
Code: Select all
shortNum := ushortNum >> 15 ? (((ushortNum ^ 0xFFFF) + 1) ^ 0xFFFFFFFFFFFFFFFF) + 1 : ushortNum
The way I look at it, we don't read and write memory in bits, but in chunks of bits. We don't manipulate memory solely using bitwise operators, but using all kinds of operators and functions, and code that generally expresses numbers in hexadecimal and decimal. I don't think that using xor, addition and unary to get from unsigned to signed is any more informative than using simple subtraction.
I came up with
ushortNum - 0x10000 on the spot, after considering the
difference (mathematically, as it turns out) between the unsigned value and the negative value.
iseahound wrote: ↑29 Mar 2024, 21:51
They're just simple facts:
That's your opinion, actually.
iseahound wrote: ↑29 Mar 2024, 23:13
Well how can you determine the position of the sign bit?
Are you ignoring the other "bit magic" methods that were demonstrated, which do not rely on the position of the sign bit?
In any case, I think the simplest way making the least assumptions is like this:
Code: Select all
MsgBox position_of_sign_bit()
position_of_sign_bit() {
Loop
if (1 << A_Index) < 0
return A_Index
}
There is no need for optimization as one can simply retain the result until the program exits; otherwise, a smarter method can be designed to rely on the assumption that the sign bit can only reasonably be at 7, 15, 31 or 63. You are screwed if the sign bit is at 7 or 15 (since you want to work with a 32-bit lParam), and << doesn't support shifting above 63 (as per the documentation), so you may as well just check if it is at 31, and when it isn't, assume it is at 63.
Code: Select all
sign_bit := (1 << 31) < 0 ? 31 : 63
MsgBox sign_bit
Then the value can be used with certainty, to calculate the number of bits that the X or Y coordinate must be shifted to put its sign bit into the right place.
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).
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".
Some may hold the
unfounded misconception that AutoHotkey 32-bit is limited to 32-bit integers, but it is more reasonable to assume that "AutoHotkey primarily uses [...] 64-bit signed integers (int64)" and "64-bit signed values are supported", completely absent any mention of AutoHotkey build or other limitations for specific operators, means that all builds support integer values in this full range, and operations will be 64-bit.
If AutoHotkey 32-bit can restrict general use of integers to 32-bit without any such restriction being documented, why not the same for DllCall and NumPut? I'm sure you didn't consider that your NumPut/NumGet method might fail due to such a reason.
It
was common knowledge (and documented) before 2010 that 64-bit integers were supported, and at that time, all versions were 32-bit. Come to think of it, 64-bit integers were supported and used for math operations even back when numbers were stored in variables
as strings.
iseahound wrote:It only takes one yet-to-be-discovered counterexample to prove that these bitwise manipulations cannot be trusted
Can you prove that the same argument does not apply equally to your own method of using NumPut and NumGet?
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.
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.
If an incoming parameter is intended to be a signed integer, any negative numbers can be revealed by following either of the following methods:
Code: 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
Source: CallbackCreate - Syntax & Usage | AutoHotkey v2
It even says "AutoHotkey natively uses signed 64-bit integers", though the example can be trusted to work regardless.
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
iseahound wrote:this is not common knowledge
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:but couldn't really find clear documentation that the integers are 64-bit.
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.
The implementation would be free to store an integer as 8-bit if it was in that range, while still supporting signed 64-bit integers as documented. The user does not need to know which data type is actually used in a variable,
because you can't get its address (surprise, we return to the original topic!).
In the context of the
bitwise operators, it does not matter what type the input value has or how it was stored, only the actual value and how the
operator itself behaves. For instance,
~ in v1 operates as 32-bit or 64-bit depending on the input value itself, not the
type of the input value (and certainly not the build).
Even just in the bit shifting documentation, there were strong indicators that they are 64-bit, without anything to suggest that this would be conditional on the build.
Seven0528 wrote: ↑30 Mar 2024, 00:02
I've never seen any mention that this is not dependent on whether it's 32-bit or 64-bit.
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.