26 de marzo de 2007

Decimales y binarios

Ya es hora de que escriba algo de lo que hago día a día en mi curro a nivel técnico. Me he visto en una situación bastante engorrosa para descifrar en un ratillo una forma de convertir números decimales (enteros de toda la vida) en un valor binario de 64 bits. Un bit es la unidad mínima de información digital, y tiene valor 0 o 1, que son los dos valores de voltaje que se usa a nivel electrónico (encendido-apagado). Estos valores forman valores complejos que se leen a una velocidad de infarto hoy en día. Es la base de cualquier unidad de información. Pero joder, usar decimales de 64 bits es algo que hasta hace relativamente poco no se ha podido hacer, porque ningún lenguaje de programación hasta Java o Visual .NET ha soportado la operación directa con tanto dígito.

Ahora, cuando quieres convertir un binario a su forma decimal, si este número tiene 64 bits (es decir, 64 unos y ceros), es una pesadilla nivel informático. La operación consiste en ir dividiendo ese número entero entre 2 y quedarse con el resultado (si es par o impar), que define si se pone un cero o un uno en esa posición (si la división es exacta, se pone un cero, de lo contrario da un uno). Si la división es inexacta, para la siguiente operación nos quedamos con el número absoluto (Es decir, que si una división nos da 12,7 o 12, 4 nos da igual: nos quedamos con el 12), para volver a dividir hasta que ya no nos sea posible, es decir, que el número a dividir sea menor o igual a 2.

Cuando ya tenemos todos los restos, invertimos la cadena al revés (el binario se lee de derecha a izquierda, como los japoneses), y ahora viene lo más divertido: si el número que queremos pasar a binario es negativo (lo que me ha pasado a mi), se hace exactamente lo mismo (dividir entre dos hasta llegar a cero), y luego invertimos ceros y unos salvo el más a la derecha. ¡Toma ya!. Y como a mi me ha costado horrores, voy a dejar el código que me he currado al respecto en Visual Basic .NET, para ver si puedo ayudar a algún alma desamparada. Lo de coger el “valor absoluto” al dividir lo he hecho muy cutre (con strings), pero me fio más que de cualquier función matemática. Y el caso es que funciona!.

Private Function DecimalABinario(ByVal ValorDecimal As Decimal) As String

Dim Binario As String = ""
Dim stringDivision As String = "", posComa As Integer = 0
Dim Valor As Decimal = ValorDecimal
Dim bEsNegativo As Boolean = (Valor < 0)

If bEsNegativo Then Valor = -Valor 'Se positiva el negativo

Do While (Valor >= 2)
Binario += IIf(esPar(Valor), "0", "1")
stringDivision = CDec((Valor / 2)).ToString.Trim
posComa = InStr(stringDivision, ",")
If (posComa > 0) Then
Valor = CDec(Left(stringDivision, posComa - 1))
Else
Valor = (Valor / 2)
End If
Loop

Binario += IIf(Valor < 2, "1", "0")

'Se invierte la cadena
Binario = InvertirCadena(Binario)

'Completamos hasta 64 Bits
Dim lenBinario As Short = Len(Binario)
Dim Maximo As Short = 64
Dim i As Integer

For i = (lenBinario + 1) To Maximo : Binario = "0" & Binario : Next i

If bEsNegativo Then ‘El ultimo dígito se cambia en caso de ser negativo
Binario = Left(Binario, 63) & IIf(Right(Binario, 1) = "0", "1", "0")
Binario = InvertirBinario(Binario)
End If

Return Binario

End Function


Private Function esPar(ByVal Numero As Decimal) As Boolean

Dim miNumero As Short = Val(Right(Numero.ToString.Trim, 1))
Dim bPar As Boolean = ((miNumero = 0) Or (miNumero = 2) Or (miNumero = 4) Or (miNumero = 6) Or (miNumero = 8))

Return bPar

End Function

Private Function InvertirCadena(ByVal strCadena As String) As String

strCadena = strCadena.Trim

Dim newCadena As String = ""
Dim PosActual As Integer = Len(strCadena)

Do While PosActual >= 1
newCadena &= Mid(strCadena, PosActual, 1)
PosActual -= 1
Loop

Return newCadena

End Function

Private Function InvertirBinario(ByVal stringBinario As String) As String

Dim NewCadena As String = "", charActual As Char = ""
Dim PosMax As Short = 0, PosActual As Short = 0
Dim i As Short = 0

stringBinario = stringBinario.Trim

PosMax = Len(stringBinario.Trim)
PosActual = 1

Do While PosActual <= PosMax
charActual = Mid(stringBinario.Trim, PosActual, 1)
Select Case charActual
Case "0"
NewCadena += "1"
Case "1"
NewCadena += "0"
End Select
PosActual += 1
Loop

Return NewCadena

End Function
Y esta va de regalo:

Private Function BinarioADecimal(ByVal sB As String) As String

'Transforma un número Binario en su valor Decimal

sB = Trim(sB)

Dim M As Decimal = 1, V As Decimal = 0, i As Integer = 0, lB As Integer = Len(sB), nA As Integer = 0

For i = lB To 1 Step -1
nA = Val(Mid(sB, i, 1).Trim)
If nA = 1 Then V += M
M *= 2
Next i

BinarioADecimal = V.ToString.Trim

End Function


Bueno, ya me he quedao a gusto con la programación de la semana. Aunque a todos los que lean esto no entiendan ni papa.

Un abrazo.