2009-05-20 7 views

risposta

12

Un paio di cose diverse. Come puoi vedere dall'esempio di Pax, devi solo cercare lo standard IEEE 754 e quindi collegare i tuoi byte nei posti giusti. L'unica avvertenza che ti darei è quella MicroSoft has deprecated RtlMoveMemory a causa del potenziale per la creazione di problemi di sicurezza del tipo di overflow. In alternativa, puoi farlo in VB "puro" con una piccola coercizione attenta usando User Defined Types e LSet. (Si noti inoltre che ci sono due tipi di 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 

Un'ultima lato nota, è anche possibile ottenere NaN in questo modo:

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

Questo è incredibilmente fantastico. Grazie mille per averlo condiviso. – bugmagnet

4

This page mostra un modo leggermente tortuoso per farlo. L'ho tagliato per abbinare ciò che la tua domanda ha richiesto, ma non ho testato a fondo. Fammi sapere se ci sono problemi. Una cosa che ho notato su quel sito è che il codice che avevano per un NaN silenzioso era sbagliato, dovrebbe iniziare la mantissa con un 1 bit: sembravano averlo confuso con un NaN di segnalazione.

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 

Esso utilizza fondamentalmente copie memoria a livello di kernel per trasferire le sequenze di bit da una matrice di byte al doppio.

Si dovrebbe tenere presente però che ci sono multipli bit valori che possono rappresentare QNAN, specificamente il bit di segno può essere 0 o 1 e tutti i bit della mantissa diversa dalla prima può anche essere zero o 1. Ciò potrebbe complicare la strategia per i confronti a meno che non sia possibile scoprire se VB6 utilizza solo uno dei pattern di bit - non influenzerà l'inizializzazione di tali valori, tuttavia, supponendo che VB6 implementa correttamente IEE754 raddoppia.

+0

Quindi stai collegando al blog del questionner originale, in cui ha pubblicato una voce con la sua migliore pugnalata un giorno prima di fare la domanda? Abbastanza giusto, è solo un po 'divertente! – MarkJ

+0

Non è solo divertente, è divertente. In realtà non sapevo che l'interrogante fosse il proprietario di quel blog in quel momento, ma c'è il suo soprannome stackoverflow proprio lì sul blog :-) Ho una doppia idea se cancellare questa risposta o meno. Se non altro, potrebbe dare un po 'di divertimento agli altri. – paxdiablo

+0

Non sono sicuro se ridere o essere imbarazzato. – bugmagnet

17

In realtà, c'è un modo molto più semplice per ottenere Infinity, -Infinity e non un numero:

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 Non l'ho mai saputo prima! Sembra che puoi anche ottenere NAN valutando 0/0? Ad ogni modo, Debug.Print dice che è -1. # IND che è diverso da 1. # INF che ottengo da 1/0. Presumo che sia NAN. – MarkJ

+0

+1 Questo è semplicemente fantastico –

+0

'Debug.Print -lfNaN' restituisce' 1. # QNAN', che presumo sia il "silenzioso" NaN (?). – Andre