Code Puzzle Thread

Discuss Autohotkey related topics here. Not a place to share code.
Forum rules
Discuss Autohotkey related topics here. Not a place to share code.
Helgef
Posts: 3597
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

02 Feb 2019, 08:25

Hello jeeswg, I get multiple msgboxes indicating incorrect results in the test script, q::, eg

Code: Select all

put

index: 3

Float

5759439474786304.0

5759438937915392.0

0x14762E00000000

0x14762DE0000000
Cheers.
User avatar
jeeswg
Posts: 6108
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

02 Feb 2019, 08:48

- I posted this in the script:
note: when converting from/to Floats,
the fractions could be *fractionally* different,
although the tests show the expected accuracy
- You'll notice that the MsgBoxes only appear for Floats, and that the numbers match to around 7 significant figures.
- Maybe even for that I can find a better fix, but we're talking maths degree level stuff here, and an extensive time commitment.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3597
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

03 Feb 2019, 15:36

the tests show the expected accuracy
It is not the correct result for recreating numput.
we're talking maths degree level stuff here
There is hardly any advanced math going on here. The bit manipulations are basic. Your approach requires knowledge about how numbers are stored, so this is not the way I expected the puzzle to be solved. The expected solution would demonstrate a knowledge about the AHK language, not about number formats. Your approach is acceptable, but currently not correctly implemented. I will give you a point for a good effort and good partial solution :clap: . I will give one point to whoever completes jeeswg's solution. I will give two points to whoever completes the full puzzle.

Cheers :wave: .
User avatar
jeeswg
Posts: 6108
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

12 Apr 2019, 15:12

I improved my script, and the MsgBox indicating a slight discrepancy when handling Floats/Doubles now appears less frequently. Around 0.7% of the time versus 24%.

Code: Select all

;slightly improved PuzPut function which handles rounding
;the MsgBox indicating a slight discrepancy now appears less frequently
;before: e.g. 2381/10000 = 0.2381 [24%]
;after: e.g. 67/10000 = 0.0067 [0.7%]

;AHK v2 script
;'PuzGet'/'PuzPut' - recreate NumGet/NumPut functionality
;where 'Puz' refers to 'Code Puzzle Thread'

;note: when converting from/to Floats,
;the fractions could be *fractionally* different,
;although the tests show the expected accuracy

;note: the maths of handling the raw binary data
;of the Floats is very fiddly, so errors are possible

;AHK v2 is used because you can convert from/to Doubles
;by using the address of a variable

w:: ;test PuzPut and PuzGet functions
VarSetCapacity(vData1, 8, 0)
VarSetCapacity(vData2, 8, 0)
NumPut(-12345678, &vData1, "Int")
PuzPut(-12345678, &vData2, "Int")
MsgBox(NumGet(&vData1, "Int"))
MsgBox(NumGet(&vData2, "Int"))
MsgBox(PuzGet(&vData1, "Int"))
return

q:: ;test PuzPut and PuzGet functions
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr", ",")
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr,Float,Double", ",")
oType := StrSplit("Float,Double", ",")
VarSetCapacity(vData1, 8)
VarSetCapacity(vData2, 8)
vCount := 0
Loop 10000
{
try
{
	vNum := Random(-2147483648, 2147483647)
	vNum *= 256**3
	vTemp := Random(1, oType.Length())
	vType := oType[vTemp]
	NumPut(vNum, &vData1, vType)
	PuzPut(vNum, &vData2, vType)
	vNum1 := NumGet(&vData1, vType)
	vNum2 := NumGet(&vData2, vType)
	vNum1X := PuzGet(&vData1, vType)
	vHex1 := Format("0x{:X}", vNum1)
	vHex2 := Format("0x{:X}", vNum2)
	vHex1X := Format("0x{:X}", vNum1X)
}
catch
	continue
;vCount++
	vIsErr := 0
	;if 0
	if !(vNum1 = vNum2)
	;if !InStr(vType, "Int64")
		MsgBox("put`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum2 "`r`n" vHex1 "`r`n" vHex2)
		, vIsErr := 1
	;if 0
	if !(vNum1 = vNum1X)
		MsgBox("get`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum1X "`r`n" vHex1 "`r`n" vHex1X)
		, vIsErr := 1
	if vIsErr
		vCount++
}
MsgBox("done " vCount)
return

;==================================================

PuzGet(vAddr, vType:="UPtr")
{
	local
	static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
	static vIsReady := 0
	if !vIsReady
	{
		oChar := {0:0}
		VarSetCapacity(vData, 1, 0)
		Loop 255
			PuzPut(A_Index, &vData, "UChar")
			, vOrd := Ord(StrGet(&vData, 1, "CP0"))
			, oChar[vOrd] := A_Index
		vIsReady := 1
	}

	if (vType = "Double")
	{
		vData := 0.0
		memcpy(&vData, vAddr, 8)
		return vData
	}

	if (vType = "Float")
	{
		vNum := PuzGet(vAddr+0, "UInt")
		vSign := !!(vNum & 0x80000000)
		vPow := ((vNum & 0x7F800000) >> 23)
		vNum := vNum & 0x7FFFFF
		if (vPow = 255)
			vPow := 2047
		else if (vPow > 0)
			vPow := (vPow - 127 + 1023)
		;else if (vPow = 0)
		;	vPow := 0
		vNum <<= (52-23)
		vTemp := (vSign ? -0x8000000000000000 : 0) | (vPow << 52) | vNum
		VarSetCapacity(vData, 8)
		PuzPut(vTemp, &vData, "Int64")
		return PuzGet(&vData, "Double")
	}

	vIsSigned := !RegExMatch(vType, "i)^U")
	vSize := oType[RegExReplace(vType, "i)^U")]
	vNum := 0
	Loop vSize
		vNum |= oChar[Ord(StrGet(vAddr+A_Index-1, 1, "CP0"))] << (A_Index*8-8)
	if vIsSigned
		if (vSize = 1) && (vNum >= 128)
			return vNum - 256
		else if (vSize = 2) && (vNum >= 32768)
			return vNum - 65536
		else if (vSize = 4) && (vNum >= 2147483648)
			return vNum - 4294967296
	return vNum
}

PuzPut(vNum, vAddr, vType:="UPtr")
{
	local
	static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
	static vIsReady := 0, vDataNull := 0
	if !vIsReady
	{
		;oChar := {0:""}
		oChar := {}
		VarSetCapacity(vDataNull, 1, 0)
		Loop 255
			oChar[A_Index] := Chr(A_Index)
		vIsReady := 1
	}

	if (vType = "Double")
	{
		vData := vNum + 0.0
		memcpy(vAddr, &vData, 8)
		return
	}

	if (vType = "Float")
	{
		vData := vNum + 0.0
		vNum := PuzGet(&vData, "Int64")

		;0x8000000000000000 (UInt64) = 0x8000000000000000 - 0x10000000000000000 (Int64) = -0x8000000000000000 (Int64)
		vSign := !!(vNum & -0x8000000000000000)
		vPow := ((vNum & 0x7FF0000000000000) >> 52)
		vNum := vNum & 0xFFFFFFFFFFFFF

		if (vPow = 2047)
			vPow := 255
		else if (vPow > 0)
			vPow := (vPow - 1023 + 127) & 0xFF
		;else if (vPow = 0)
		;	vPow := 0
		vBit := (vNum >> (52-24)) & 0x1 ;truncate number, but if bit is 1, round up
		vNum >>= (52-23)
;MsgBox vBit
;if (vBit)
;throw
		;handle rounding:
		;if 0
		if vBit
		{
			vTemp := 1
			Loop 22
			{
				if !(vNum & vTemp)
				{
					vNum |= vTemp
					vNum ^= vTemp-1
					break
				}
				vTemp <<= 1
			}
			if (vTemp = 2**22)
				vNum := 0, vPow += 1
		}

		vTemp := (vSign << 31) | (vPow << 23) | vNum
		PuzPut(vTemp, vAddr+0, "UInt")
		return
	}

	vSize := oType[RegExReplace(vType, "i)^U")]
	Loop Min(vSize, 7)
		vTemp := vNum & (0xFF << (A_Index*8-8))
		, vTemp >>= (A_Index*8-8)
		, (vTemp=0) ? memcpy(vAddr+A_Index-1, &vDataNull, 1) : memcpy(vAddr+A_Index-1, oChar.GetAddress(vTemp), 1)
	;0xFF00000000000000 (UInt64) = (0xFF00000000000000 - 0x10000000000000000) (Int64) = -0x100000000000000 (Int64)
	if (vSize = 8)
		vTemp := SubStr(Format("0x{:016X}", vNum & -0x100000000000000), 1, 4)
		, vTemp := Integer(vTemp)
		, (vTemp=0) ? memcpy(vAddr+7, &vDataNull, 1) : memcpy(vAddr+7, oChar.GetAddress(vTemp), 1)
}

memcpy(dest, src, count){
	/*
	void *memcpy(
		void *dest,
		const void *src,
		size_t count
	);
	url: https://msdn.microsoft.com/en-us/library/dswaw1wk.aspx (memcpy)
	*/
	return dllcall("MSVCRT.dll\memcpy", "ptr", dest, "ptr", src, "ptr", count, "cdecl")
}

;prepend Z to MsgBox, and it becomes a NoOp
ZMsgBox(oParams*)
{
}
To explain rounding in binary, here are 2 examples in decimal, where I want to remove the last digit, but since the last digit is 5 or higher, rounding up must be done:

0.999000999
0.99900100

0.999999999
1.00000000

In binary, if the last bit is 1, rounding up must be done. You start at the rightmost bit, and go left until you find a 0 bit, that 0 bit becomes a 1 bit, and the bits after it all become 0 bits.

0.111000111
0.11100100

0.111111111
1.00000000
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3597
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 07:44

I don't see any explanation. Why are you rounding up?
User avatar
jeeswg
Posts: 6108
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 07:53

When you convert from a double to a float, decreasing precision, you have to round the number. Rounding down is simply truncation, rounding up, which I explained above, involves a bit of bit magic.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3597
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 08:06

When you convert from a double to a float, decreasing precision, you have to round the number.
Why?
User avatar
jeeswg
Posts: 6108
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 08:30

- In my script, I take advantage of the fact that AutoHotkey natively uses doubles. I need to convert those doubles to floats. So the question is: how do you convert a double to a float.
- What you need to do is take the 3 numbers: sign, exponent, significand, and adjust them to decrease the precision, AFAIK this involves rounding. Initially I was only rounding down, and the script was getting around 50% of the conversions slightly mismatched. When I started handling rounding up, that figure was more like 1%.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3597
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 08:44

So the question is: how do you convert a double to a float.
Initially I was only rounding down, and the script was getting around 50% of the conversions slightly mismatched. When I started handling rounding up, that figure was more like 1%.
So you should conclude that either, rounding up isn't the answer, or you are doing it wrong, or both.
User avatar
jeeswg
Posts: 6108
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 09:01

- The only thing I can conclude currently is that: the floats/doubles have not always been matching up exactly. I cannot even conclude that they *should* always be matching up exactly.
- If by training or by experimentation you have something to add on the matter, I would suggest you do so, either by clue, or just by giving a complete answer.
- I have limited time, and over the next few days I will be completing some mathematical tutorials, so any input would be better now rather than later.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “General Discussion”

Who is online

Users browsing this forum: No registered users and 1 guest