Search the Community
Showing results for tags '_isnumber'.
-
I had a dissatisfaction with IsNumber so this function was born: ; ========================================================================================= ; UDF: DataTypeChecker.au3 (strict-hex + numeric formats + IEEE754 + self-test: _RunSelfTest() ) ; Author: Dao Van Trong - TRONG.PRO ; Notes: ; - Type codes start at 1 (String = 1). IEEE-754 subtypes continue up to 24. ; - Unknown returns 0 (no numeric code assigned). ; - String detection order: Hex (0x... or bare even-length [0-9A-Fa-f]) → ; Numeric (US/EU/Thousands/Simple-EU) → Boolean-like → fallback String. ; - HWND is checked before Ptr to avoid misclassification. ; ========================================================================================= ; -------- Global Type Codes (1..24) -------- Global Const $TYPE_STRING = 1 Global Const $TYPE_INT32 = 2 Global Const $TYPE_INT64 = 3 Global Const $TYPE_DOUBLE = 4 Global Const $TYPE_BINARY = 5 Global Const $TYPE_BOOLEAN = 6 Global Const $TYPE_ARRAY = 7 Global Const $TYPE_MAP = 8 Global Const $TYPE_PTR = 9 Global Const $TYPE_STRUCT = 10 Global Const $TYPE_HWND = 11 Global Const $TYPE_OBJECT = 12 Global Const $TYPE_KEYWORD = 13 Global Const $TYPE_FUNCTION = 14 Global Const $TYPE_USERFUNCTION = 15 ; IEEE-754 sub-categories for Double Global Const $TYPE_POS_ZERO = 16 Global Const $TYPE_NEG_ZERO = 17 Global Const $TYPE_DENORMAL_POS = 18 Global Const $TYPE_DENORMAL_NEG = 19 Global Const $TYPE_NORMAL_POS = 20 Global Const $TYPE_NORMAL_NEG = 21 Global Const $TYPE_POS_INFINITY = 22 Global Const $TYPE_NEG_INFINITY = 23 Global Const $TYPE_NAN = 24 ; Unknown uses 0 (no constant) ; -------- Code → Name -------- Func _GetDataTypeName($i) Select Case $i = $TYPE_STRING Return "String" Case $i = $TYPE_INT32 Return "Int32" Case $i = $TYPE_INT64 Return "Int64" Case $i = $TYPE_DOUBLE Return "Double/Float" Case $i = $TYPE_BINARY Return "Binary/Hex" Case $i = $TYPE_BOOLEAN Return "Boolean" Case $i = $TYPE_ARRAY Return "Array" Case $i = $TYPE_MAP Return "Map" Case $i = $TYPE_PTR Return "Pointer" Case $i = $TYPE_STRUCT Return "DLL Struct" Case $i = $TYPE_HWND Return "Window Handle" Case $i = $TYPE_OBJECT Return "Object" Case $i = $TYPE_KEYWORD Return "Keyword" Case $i = $TYPE_FUNCTION Return "Function" Case $i = $TYPE_USERFUNCTION Return "UserFunction" Case $i = $TYPE_POS_ZERO Return "+Zero" Case $i = $TYPE_NEG_ZERO Return "-Zero" Case $i = $TYPE_DENORMAL_POS Return "deNormal (+)" Case $i = $TYPE_DENORMAL_NEG Return "deNormal (−)" Case $i = $TYPE_NORMAL_POS Return "Normal (+)" Case $i = $TYPE_NORMAL_NEG Return "Normal (−)" Case $i = $TYPE_POS_INFINITY Return "+Infinity" Case $i = $TYPE_NEG_INFINITY Return "-Infinity" Case $i = $TYPE_NAN Return "NaN" Case Else Return "Unknown" EndSelect EndFunc ;==>_GetDataTypeName ; -------- IEEE-754 classifier for Double (robust for 64-bit via hi/low dwords) -------- Func _CheckIEEE754Category($dVal) ; Overlay: write the double, then read two dwords at the same address Local $tDouble = DllStructCreate("double") DllStructSetData($tDouble, 1, $dVal) Local $p = DllStructGetPtr($tDouble) Local $tWords = DllStructCreate("dword;dword", $p) ; [1]=low, [2]=high on little-endian Local $low = DllStructGetData($tWords, 1) Local $high = DllStructGetData($tWords, 2) Local $sign = BitAND(BitShift($high, 31), 1) Local $exp = BitAND(BitShift($high, 20), 0x7FF) Local $fracHigh = BitAND($high, 0x000FFFFF) Local $fracLow = $low Local $fracIsZero = ($fracHigh = 0 And $fracLow = 0) If $exp = 0 Then If $fracIsZero Then Return ($sign ? $TYPE_NEG_ZERO : $TYPE_POS_ZERO) Else Return ($sign ? $TYPE_DENORMAL_NEG : $TYPE_DENORMAL_POS) EndIf ElseIf $exp = 0x7FF Then If $fracIsZero Then Return ($sign ? $TYPE_NEG_INFINITY : $TYPE_POS_INFINITY) Else Return $TYPE_NAN EndIf Else Return ($sign ? $TYPE_NORMAL_NEG : $TYPE_NORMAL_POS) EndIf EndFunc ;==>_CheckIEEE754Category ; -------- Decide Int32 vs Int64 by range -------- Func _IntCodeFromNumber($n) If $n >= -2147483648 And $n <= 2147483647 Then Return $TYPE_INT32 Else Return $TYPE_INT64 EndIf EndFunc ;==>_IntCodeFromNumber ; -------- Normalize & classify numeric strings -------- ; Returns: type code (2/3 for ints OR IEEE-754 sub-codes 16..24 for doubles) Func _ParseNumericString($s) ; US: 1,234.56 → remove commas, parse with dot If StringRegExp($s, '^[+-]?\d{1,3}(,\d{3})*(\.\d+)?$') Then Local $u = StringReplace($s, ",", "") If StringInStr($u, ".") Then Return _CheckIEEE754Category(Number($u)) Else ; For grouped integers (US), we follow user's expectation: ; treat as Double category (Normal +/-) rather than Int32/Int64 Return _CheckIEEE754Category(Number($u)) EndIf EndIf ; EU: 1.234,56 → remove dots, replace comma with dot If StringRegExp($s, '^[+-]?\d{1,3}(\.\d{3})*(,\d+)?$') Then Local $e = StringReplace(StringReplace($s, ".", ""), ",", ".") If StringInStr($s, ",") Then Return _CheckIEEE754Category(Number($e)) Else ; Grouped integer (EU) → follow same rule: classify via IEEE-754 Return _CheckIEEE754Category(Number($e)) EndIf EndIf ; Thousands only: 1,234 or 1.234 → strip separators If StringRegExp($s, '^[+-]?\d{1,3}([,\.]\d{3})+$') Then Local $t = StringReplace(StringReplace($s, ",", ""), ".", "") ; Per user's expected results: treat grouped integer as Double category Return _CheckIEEE754Category(Number($t)) EndIf ; Simple EU decimals: 123,45 → replace comma with dot If StringRegExp($s, '^[+-]?\d+,\d+$') Then Local $d = StringReplace($s, ",", ".") Return _CheckIEEE754Category(Number($d)) EndIf ; Plain ints/floats (no separators) If StringIsInt($s) Then Return _IntCodeFromNumber(Number($s)) If StringIsFloat($s) Then Return _CheckIEEE754Category(Number($s)) ; Not numeric Return 0 EndFunc ;==>_ParseNumericString ; -------- Parse strings: Hex → Numeric → Boolean-like → String -------- Func _ParseStringValue($s) $s = StringStripWS($s, 8) ; Hex with prefix 0x, body must have even length If StringRegExp($s, "^0[xX][0-9A-Fa-f]+$") Then Local $hexBody = StringMid($s, 3) If Mod(StringLen($hexBody), 2) = 0 Then Return $TYPE_BINARY EndIf ; Bare hex (0-9 A-F), even length accepted as hex (as requested) If StringRegExp($s, "^[0-9A-Fa-f]+$") Then If Mod(StringLen($s), 2) = 0 Then Return $TYPE_BINARY EndIf ; Numeric formats Local $numCode = _ParseNumericString($s) If $numCode <> 0 Then Return $numCode ; Boolean-like strings Switch StringLower($s) Case "true", "false" Return $TYPE_BOOLEAN EndSwitch ; Fallback string Return $TYPE_STRING EndFunc ;==>_ParseStringValue ; -------- Public: get type code for any value -------- Func _GetDataType($v) ; AutoIt-specific first (order matters) If IsArray($v) Then Return $TYPE_ARRAY If IsMap($v) Then Return $TYPE_MAP If IsHWnd($v) Then Return $TYPE_HWND If IsPtr($v) Then Return $TYPE_PTR If IsDllStruct($v) Then Return $TYPE_STRUCT If IsObj($v) Then Return $TYPE_OBJECT If IsKeyword($v) Then Return $TYPE_KEYWORD Local $sType = VarGetType($v) If IsFunc($v) Then If $sType = "UserFunction" Then Return $TYPE_USERFUNCTION Else Return $TYPE_FUNCTION EndIf EndIf ; Native primitives If $sType = "Int32" Then Return $TYPE_INT32 If $sType = "Int64" Then Return $TYPE_INT64 If $sType = "Double" Then Return _CheckIEEE754Category($v) If IsBinary($v) Then Return $TYPE_BINARY If IsBool($v) Then Return $TYPE_BOOLEAN ; Strings If IsString($v) Then Return _ParseStringValue($v) ; Unknown Return 0 EndFunc ;==>_GetDataType ; -------- Helper: is any numeric type? (native or string-based) -------- Func _IsNumber($v) Local $c = _GetDataType($v) Return ($c = $TYPE_INT32 Or $c = $TYPE_INT64 Or $c = $TYPE_DOUBLE Or _ $c = $TYPE_POS_ZERO Or $c = $TYPE_NEG_ZERO Or _ $c = $TYPE_DENORMAL_POS Or $c = $TYPE_DENORMAL_NEG Or _ $c = $TYPE_NORMAL_POS Or $c = $TYPE_NORMAL_NEG Or _ $c = $TYPE_POS_INFINITY Or $c = $TYPE_NEG_INFINITY Or _ $c = $TYPE_NAN) EndFunc ;==>_IsNumber ; ========================================================================================= ; SELF-TEST (English logs, prefixes: '+' OK, '!' ERROR, '-' INFO) ; ========================================================================================= Func _AddTest(ByRef $arr, $val, $expectedName, $label) ReDim $arr[UBound($arr) + 1][3] $arr[UBound($arr) - 1][0] = $val $arr[UBound($arr) - 1][1] = $expectedName $arr[UBound($arr) - 1][2] = $label EndFunc ;==>_AddTest Func __TestFunc() Return 1 EndFunc ;==>__TestFunc Func _RunSelfTest() ConsoleWrite("- Starting self-test..." & @CRLF) Local $tests[0][3] ; --- String fallback --- _AddTest($tests, "Hello", "String", "Plain string") ; Per requirement: bare [0-9A-F] with even length is HEX _AddTest($tests, "123ABC", "Binary/Hex", "Bare hex (even)") ; --- Int32 / Int64 --- _AddTest($tests, 42, "Int32", "Integer") _AddTest($tests, -999, "Int32", "Negative int") _AddTest($tests, 9223372036854775807, "Int64", "Max Int64") _AddTest($tests, -9223372036854775808, "Int64", "Min Int64") ; --- Double / IEEE754 normal --- _AddTest($tests, 3.14159, "Normal (+)", "Positive double") _AddTest($tests, -2.71828, "Normal (−)", "Negative double") ; --- Binary/Hex --- _AddTest($tests, "0x1A2B", "Binary/Hex", "Prefixed hex string") _AddTest($tests, "DEADBEEF", "Binary/Hex", "Bare hex string") ; --- Boolean --- _AddTest($tests, True, "Boolean", "True literal") _AddTest($tests, "false", "Boolean", "Boolean-like string") ; --- Array --- Local $arr[2] = [1, 2] _AddTest($tests, $arr, "Array", "Simple array") ; --- Map (correct syntax) --- Local $mMap[] $mMap["Key"] = "Value" _AddTest($tests, $mMap, "Map", "Map object") ; --- Ptr --- Local $p = Ptr(0x123456) _AddTest($tests, $p, "Pointer", "Pointer value") ; --- DllStruct --- Local $st = DllStructCreate("int;char[4]") _AddTest($tests, $st, "DLL Struct", "DllStruct sample") ; --- HWND (create GUI to ensure valid handle) --- Local $hGUI = GUICreate("DT Test", 1, 1, -100, -100) GUISetState(@SW_SHOW, $hGUI) _AddTest($tests, $hGUI, "Window Handle", "GUI handle") ; --- Object --- Local $o = ObjCreate("Scripting.Dictionary") _AddTest($tests, $o, "Object", "COM object") ; --- Keyword --- _AddTest($tests, Default, "Keyword", "Keyword Default") _AddTest($tests, Null, "Keyword", "Keyword Null") ; --- Function & UserFunction --- _AddTest($tests, Eval, "Function", "Builtin function") _AddTest($tests, __TestFunc, "UserFunction", "User-defined function") ; --- IEEE754 specials --- _AddTest($tests, +0.0, "+Zero", "Positive zero") _AddTest($tests, -0.0, "-Zero", "Negative zero") _AddTest($tests, (1.0 / 0.0), "+Infinity", "Positive infinity") _AddTest($tests, (-1.0 / 0.0), "-Infinity", "Negative infinity") _AddTest($tests, (0.0 / 0.0), "NaN", "Not-a-Number") ; --- Numeric strings: US/EU/Thousands/Simple-EU --- _AddTest($tests, "1,234.56", "Normal (+)", "US numeric string") _AddTest($tests, "1.234,56", "Normal (+)", "EU numeric string") _AddTest($tests, "1,234", "Normal (+)", "Thousand separator comma") _AddTest($tests, "1.234", "Normal (+)", "Thousand separator dot") _AddTest($tests, "123,45", "Normal (+)", "EU decimal comma") ; --- Run all --- For $i = 0 To UBound($tests) - 1 Local $input = $tests[$i][0] Local $expected = $tests[$i][1] Local $label = $tests[$i][2] Local $code = _GetDataType($input) Local $got = _GetDataTypeName($code) If $got = $expected Then ConsoleWrite("- " & '_IsNumber(' & $input & ") -> " & _IsNumber($input) & @CRLF) ConsoleWrite("+ " & $label & ' "' & $input & '" -> (' & $code & ') ' & $got & @CRLF) Else ConsoleWrite("! " & $label & ' "' & $input & '" -> (' & $code & ') ' & $got & " | Expected: " & $expected & @CRLF) EndIf Next ConsoleWrite("- Self-test complete." & @CRLF) ; Clean up GUI created for HWND test GUIDelete($hGUI) EndFunc ;==>_RunSelfTest ; ========================================================================================= ; Self-test routine: kiểm tra các giá trị mẫu và in PASS/FAIL ; ========================================================================================= Func _RunSelfTest2() Local $tests = [ _ ["String literal", "Hello", $TYPE_STRING], _ ["Int32 literal", 123, $TYPE_INT32], _ ["Int64 literal", 5000000000, $TYPE_INT64], _ ["Double normal +", 3.14, $TYPE_NORMAL_POS], _ ["Double normal −", -2.5, $TYPE_NORMAL_NEG], _ ["+Zero (double)", 0.0, $TYPE_POS_ZERO], _ ["-Zero (double)", -0.0, $TYPE_NEG_ZERO], _ ["Subnormal", Number("1e-320"), $TYPE_DENORMAL_POS], _ ["+Infinity", 1 / 0, $TYPE_POS_INFINITY], _ ["-Infinity", -1 / 0, $TYPE_NEG_INFINITY], _ ["NaN", 0 / 0, $TYPE_NAN], _ ["Hex 0x…", "0x1A2B", $TYPE_BINARY], _ ["Bare hex", "FACE", $TYPE_BINARY], _ ["US number", "1,234.56", $TYPE_NORMAL_POS], _ ["EU number", "1.234,56", $TYPE_NORMAL_POS], _ ["Thou only", "1,234", $TYPE_NORMAL_POS], _ ["Simple EU dec", "123,45", $TYPE_NORMAL_POS], _ ["Bool-like true", "true", $TYPE_BOOLEAN], _ ["Bool-like false", "false", $TYPE_BOOLEAN] _ ] Local $allOK = True ConsoleWrite(@CRLF & "---- Running DataTypeChecker Self-Test ----" & @CRLF) For $i = 0 To UBound($tests) - 1 Local $desc = $tests[$i][0] Local $val = $tests[$i][1] Local $exp = $tests[$i][2] Local $got = _GetDataType($val) Local $name = _GetDataTypeName($got) If $got = $exp Then ConsoleWrite("- _IsNumber(" & $val & ') -> ' & _IsNumber($val) & @CRLF) ConsoleWrite("+ PASS: " & $desc & ' "' & $val & '" -> ' & $name & " (" & $got & ")" & @CRLF) Else ConsoleWrite("! FAIL: " & $desc & ' "' & $val & '" -> Expected ' & _ _GetDataTypeName($exp) & " (" & $exp & "), Got " & _ $name & " (" & $got & ")" & @CRLF) $allOK = False EndIf Next ConsoleWrite("---- Self-Test " & ($allOK ? "+ PASSED" : "! FAILED") & " ----" & @CRLF & @CRLF) Return $allOK EndFunc ;==>_RunSelfTest2 ; Uncomment to run self-test on execute ; _RunSelfTest() ; _RunSelfTest2()