2009-05-20 11 views

Respuesta

12

Un par de cosas diferentes. Como puede ver en el ejemplo de Pax, solo necesita buscar el estándar IEEE 754 y luego conectar sus bytes en los lugares correctos. La única precaución que le daría es que MicroSoft has deprecated RtlMoveMemory debido a su potencial para crear problemas de seguridad del tipo de desbordamiento. Como alternativa puede lograr esto en VB "puro" con un poco de coerción cuidadosa usando Tipos definidos por el usuario y LSet. (Tenga en cuenta también que hay dos tipos de NaN.)

Option Explicit 

Public Enum abIEEE754SpecialValues 
    abInfinityPos 
    abInfinityNeg 
    abNaNQuiet 
    abNaNSignalling 
    abDoubleMax 
    abDoubleMin 
End Enum 

Private Type TypedDouble 
    value As Double 
End Type 

Private Type ByteDouble 
    value(7) As Byte 
End Type 

Public Sub Example() 
    MsgBox GetIEEE754SpecialValue(abDoubleMax) 
End Sub 

Public Function GetIEEE754SpecialValue(ByVal value As abIEEE754SpecialValues) As Double 
    Dim dblRtnVal As Double 
    Select Case value 
    Case abIEEE754SpecialValues.abInfinityPos 
     dblRtnVal = BuildDouble(byt6:=240, byt7:=127) 
    Case abIEEE754SpecialValues.abInfinityNeg 
     dblRtnVal = BuildDouble(byt6:=240, byt7:=255) 
    Case abIEEE754SpecialValues.abNaNQuiet 
     dblRtnVal = BuildDouble(byt6:=255, byt7:=255) 
    Case abIEEE754SpecialValues.abNaNSignalling 
     dblRtnVal = BuildDouble(byt6:=248, byt7:=255) 
    Case abIEEE754SpecialValues.abDoubleMax 
     dblRtnVal = BuildDouble(255, 255, 255, 255, 255, 255, 239, 127) 
    Case abIEEE754SpecialValues.abDoubleMin 
     dblRtnVal = BuildDouble(255, 255, 255, 255, 255, 255, 239, 255) 
    End Select 
    GetIEEE754SpecialValue = dblRtnVal 
End Function 

Public Function BuildDouble(_ 
    Optional byt0 As Byte = 0, _ 
    Optional byt1 As Byte = 0, _ 
    Optional byt2 As Byte = 0, _ 
    Optional byt3 As Byte = 0, _ 
    Optional byt4 As Byte = 0, _ 
    Optional byt5 As Byte = 0, _ 
    Optional byt6 As Byte = 0, _ 
    Optional byt7 As Byte = 0 _ 
    ) As Double 
    Dim bdTmp As ByteDouble, tdRtnVal As TypedDouble 
    bdTmp.value(0) = byt0 
    bdTmp.value(1) = byt1 
    bdTmp.value(2) = byt2 
    bdTmp.value(3) = byt3 
    bdTmp.value(4) = byt4 
    bdTmp.value(5) = byt5 
    bdTmp.value(6) = byt6 
    bdTmp.value(7) = byt7 
    LSet tdRtnVal = bdTmp 
    BuildDouble = tdRtnVal.value 
End Function 

Una última nota al margen, también se puede obtener NaN de esta manera:

Public Function GetNaN() As Double 
    On Error Resume Next 
    GetNaN = 0/0 
End Function 
+0

Esto es increíblemente genial. Muchas gracias por compartirlo. – bugmagnet

4

This page muestra una forma ligeramente tortuosa de hacerlo. Lo recorté para que coincida con lo que pidió su pregunta, pero no lo he probado a fondo. Avíseme si hay algún problema. Una cosa que noté en ese sitio es que el código que tenían para un NaN silencioso era incorrecto, debería comenzar la mantisa con un bit de 1 bit, parecían haberse confundido con un NaN de señalización.

Public NegInfinity As Double 
Public PosInfinity As Double 
Public QuietNAN As Double 

Private Declare Sub CopyMemoryWrite Lib "kernel32" Alias "RtlMoveMemory" (_ 
    ByVal Destination As Long, source As Any, ByVal Length As Long) 

' IEEE754 doubles:               ' 
' seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm ' 
' s = sign                ' 
' e = exponent               ' 
' m = mantissa               ' 
' Quiet NaN: s = x, e = all 1s, m = 1xxx...        ' 
' +Inf  : s = 0, e = all 1s, m = all 0s.        ' 
' -Inf  : s = 1, e = all 1s, m = all 0s.        ' 

 

Public Sub Init() 
    Dim ptrToDouble As Long 
    Dim byteArray(7) As Byte 
    Dim i As Integer 

    byteArray(7) = &H7F 
    For i = 0 To 6 
     byteArray(i) = &HFF 
    Next 
    ptrToDouble = VarPtr(QuietNAN) 
    CopyMemoryWrite ptrToDouble, byteArray(0), 8 

    byteArray(7) = &H7F 
    byteArray(6) = &HF0 
    For i = 0 To 5 
     byteArray(i) = 0 
    Next 
    ptrToDouble = VarPtr(PosInfinity) 
    CopyMemoryWrite ptrToDouble, byteArray(0), 8 

    byteArray(7) = &HFF 
    byteArray(6) = &HF0 
    For i = 0 To 5 
     byteArray(i) = 0 
    Next 
    ptrToDouble = VarPtr(NegInfinity) 
    CopyMemoryWrite ptrToDouble, byteArray(0), 8 
End Sub 

Se utiliza básicamente copias de la memoria a nivel de kernel para transferir los patrones de bits a partir de una matriz de bytes en el doble.

Usted debe tener en cuenta sin embargo que hay bits valores múltiples que pueden representar QNAN, específicamente el bit de signo puede ser 0 o 1 y todos los bits de la mantisa que no sea el primero también puede ser cero o 1. Esto puede complicar su estrategia para las comparaciones a menos que pueda descubrir si VB6 solo usa uno de los patrones de bits; sin embargo, no afectará la inicialización de esos valores, suponiendo que VB6 implemente correctamente el IEE754 se duplica.

+0

Así que va a asociar al blog de la questionner original, donde él publicó una entrada con su mejor puñalada un día antes de hacer la pregunta? ¡Bastante, es divertido! – MarkJ

+0

Eso no es solo divertido, es gracioso. En realidad, no sabía que el que preguntaba era el propietario de ese blog en ese momento, pero hay su apodo de stackoverflow allí mismo en el blog :-) No estoy seguro de si eliminar esta respuesta o no. Si nada más, puede proporcionar algo de diversión a los demás. – paxdiablo

+0

No estoy seguro de si reír o avergonzarme. – bugmagnet

17

En realidad, hay una manera mucho más simple para obtener Infinity, -Infinity y no un número:

public lfNaN as Double ' or As Single 
public lfPosInf as Double 
public lfNegInf as Double 

on error resume next ' to ignore Run-time error '6': Overflow and '11': Division by zero 
lfNaN = 0/0  ' -1.#IND 
lfPosInf = 1/0  ' 1.#INF 
lfNegInf = -1/0  ' -1.#INF 

on error goto 0   ' optional to reset the error handler 
+0

+1 ¡Nunca lo había sabido antes! ¿Parece que también puedes obtener NAN al evaluar 0/0? De todos modos, Debug.Print dice que es -1. # IND que es diferente del 1. # INF que obtengo de 1/0. Supongo que es NAN. – MarkJ

+0

+1 Eso es simplemente impresionante –

+0

'Debug.Print -lfNaN' da' 1. # QNAN', que supongo que es el NaN "silencioso" (?). – Andre