Code archives/Miscellaneous/Val()

This code has been declared by its author to be Public Domain code.

Download source code

Val() by Barliesque2004
Convert strings containing Hex, Binary or Floating/Integer Decimal values to a numeric value.
;****************************************************
;**
;**  Val()  -  Converts a numeric value in a string
;**            to a decimal value.  Strings can contain:
;**               Integers
;**               Floating point numbers
;**               Hexadecimal
;**               Binary
;**
;**   For Hex values use any of these formats:
;**         0xFF  $FF  #FF
;**
;**   For Binary use this format:
;**         %11001010
;**
;**   NOTE:  Because this function returns all values
;**          as floating point, there is a limit to the
;**          range of values it can handle accurately.
;**          If you need integers that are larger than
;**          16777215 or smaller than -16777215 (12 bits)
;**          then you should remove the # from the
;**          function declaration and from the variable
;**          Num# to get the fullest accuracy for large
;**          integers.
;**
;**   Courtesy of Barliesque  :)
;**
;***************************************************

Local Value%

Print Val("37")
Print Val("3-7")
Print Val("50.1")
Print "0xFFFF = " + Int(Val("0xFFFF"))
Print "#E5 = " + Val("#E5")
Print "$FF = " + Val("$FF")
Value = Val("%10010010")
Print "%10010010 = " + Value
WaitKey

End

;****************************************************

Function Val#(StringNumeric$)

   Local Num# = 0
   Local Hex1 = ((Left$(StringNumeric$,1)="#") Or (Left$(StringNumeric$,1)="$"))
   Local Hex2 = (Left$(StringNumeric$,2)="0x")
   Local Binary = (Left$(StringNumeric$,1)="%")
   Local i,c
   
   If Hex1 Or Hex2
      StringNumeric$ = Upper(StringNumeric$)
      For i=(Hex1 + (Hex2 * 2) + 1) To Len(StringNumeric$) 
      	c = asc(Mid$(StringNumeric$,i,1))
         Select true
            Case (c>=48 and c<=57)  ;0 through 9
               Num# = (Num# * 16) + c-48
            Case (c>=65 and c<=70)  ;A through F
               Num# = (Num# * 16) + c-55
            Default
               Return Num#                        
         End Select
      Next
   Else
      If Binary
         For i=2 To Len(StringNumeric$) 
            Select Mid$(StringNumeric$,i,1)
               Case "1"
                  Num# = (Num# * 2) + 1
               Case "0"
                  Num# = (Num# * 2)
               Default
                  Return Num#                        
            End Select
         Next
      Else
         Num# = StringNumeric$
      EndIf
   EndIf
   Return Num#
   
End Function

Comments

Rob Farley2004
Without looking through your code too much, however...

You've got a big case statement which could be replaced with

Instr("1234567890ABCDEF", mid(stringnumerics$,c,1))

Would return 0-16 which might make your life a little easier.


Barliesque2004
Nah. Don't think that would change things for the better. I'm pretty sure the select/case structure is about as optimised as it's going to get.

Other things could probably be optimised a teensy bit, like putting Mid$(StringNumeric$,c,1) into a variable, rather than calling that statement... well, twice actually. ...Nah, probably fine as it is.

[EDIT] On second though, that select statement wasn't above a little bit of optimization. ;)


Floyd2004
There's really no way to make a single Blitz function that evaluates all types of numbers.

If it returns an integer value then it can't handle anything after a decimal point, or extremely large values.

If it returns a floating point number then it can't accurately represent an integer with too many digits.
test$ = "1234567890"

value = test
Print value
value = Val(test)
Print value

WaitKey
Try that with your Val() function, which returns a float.

This is why Abs(), for example, is a built-in Blitz operator, not a function. Abs(1) is an integer while Abs(1.0) is a float.


aab2004
i made a simple function that converts strings such as '1', '1255' etc into ints by finding their length, then for each letter finding the int of the char's ascii, minus the ascii for 0, then multiplying it by 10^(its position from the length of the $), and then adding up that for each char.

Its alot simpler, but of course only converts to int, although thenagain you could just divide anything after a decimal point by 10.


Barliesque2004
@Floyd:
Well you got me there. You're right. Frankly though, I'm not very concerned by that limitation for this function. It succesfully handles a very wide range of values and number bases in a single function. I designed it to extract information from a text file script for a game I'm working on. I want to include color values in the script, so the maximum value I need is 0xFFFFFF which appears to be the maximum value this function handles accurately. Phwew!

Thank you for pointing out that limitation. It is good to know about. :) I'll add a note to the top of the code.

@aab:
I've used exactly that technique for handling Hex and Binary, but for decimal integers and floats that isn't necessary. Look more closely at how the function works and you'll see that decimal conversions are handled in just one statement:

Num# = StringNumeric$
If you make the function only return integer values, then this one line will also return the huge number Floyd tested with above.

P.S. Following Rob Farley's comment above, I've just made a small optimization to the Select/Case structure in the Hex conversion.


virtlands2012
[ Val() by Barliesque ]

It's all fascinating; I'm pretty sure there's a way
to substantially improve the algorithm and code of the
converter.

One way to speed up the code would be to create
a hex-lookup-table, will get back to you on this
if I can think of the details.


Code Archives Forum