Search code examples
vbscriptwshdouble-precisionscientific-notation

Converting a number to normalized scientific notation


I'm trying to create a method converting a number to normalized scientific notation, here is the code that I'm using to calculate mantissa and exponent:

ConvNSN 1000, M, P
MsgBox M & "e" & P

Sub ConvNSN(N, M, P)
    If N = 0 Then
        P = 0
        M = 0
    Else
        P = Int(Log(Abs(N)) / Log(10))
        M = N / 10 ^ P
    End If
End Sub

The problem I am facing is that this code gives wrong exponent value for some numbers, eg 1000, 10E+6, 10E+9, 10E+12, 10E+13, etc... Exactly for 1000 converted should be 1e3, but not 10e2. It's obvious that the same problem with numbers, whose logarithms are close to an integer value, like Log(1 - 5.55111512312578E-17) / Log(10), which result is 0, however 1 - 5.55111512312578E-17 less then 1, and result has to be negative.

How can I get rid of Double type imprecision, and get this code to work properly?

UPDATE

I assume the fastest and quite accurate method to calculate mantissa and exponent of number in normalized scientific notation may be as follows:

Sub ConvNSN(N, M, P)
    Dim A
    If N = 0 Then
        P = 0
        M = 0
        Exit Sub
    End If
    A = Abs(N)
    If A < 1 Then
        P = Int(Log(A) / Log(10))
    Else
        P = Int(Log(A) / Log(10) * (2 + Log(.1) / Log(10)))
    End If
    M = N / 10 ^ P
End Sub

Or another one, based on @Bob's solution:

Sub ConvNSN(N, M, P)
    If N = 0 Then
        P = 0
        M = 0
    Else
        P = Int(Log(Abs(N)) / Log(10))
        M = N / 10 ^ P
    End If
    If Abs(M) = "10" Then
        M = M / 10
        P = P + 1
    End If
End Sub

First one slightly faster. Both of them process exponent from -322 to 308, but return not normalized mantissa with powers of 10 less then -310. I have not tested them yet with numbers, whose logarithms are a marginally less but very close to an integer values.

UPDATE 2

I decided to attach here an extra Sub ConvEN(), allowing to represent a number in engineering notation with SI prefixes from "p" to "T":

N = .0000456789
ConvNSN N, M, P
M = Round(M, 2)
ConvEN M, P, R, S
MsgBox R & " " & S & "Units"

Sub ConvNSN(N, M, P)
    Dim A
    If N = 0 Then
        P = 0
        M = 0
        Exit Sub
    End If
    A = Abs(N)
    If A < 1 Then
        P = Int(Log(A) / Log(10))
    Else
        P = Int(Log(A) / Log(10) * (2 + Log(.1) / Log(10)))
    End If
    M = N / 10 ^ P
End Sub

Sub ConvEN(M, P, R, S)
    DIM Q, P3
    Q = int(P / 3)
    P3 = Q * 3
    If Q >= -4 And Q <= 4 Then
        S = Array("p", "n", ChrW(&H03BC), "m", "", "k", "M", "G", "T")(Q + 4)
    Else
        S = "e" & P3 & " "
    End If
    R = M * 10 ^ (P - P3)
End Sub

Solution

  • Try this:

    ConvNSN 1000, M, P
    MsgBox M & "E" & P
    
    ConvNSN 0.00000000000000001234, M, P
    MsgBox M & "E" & P
    
    ConvNSN -0.00000000000000001234, M, P
    MsgBox M & "E" & P
    
    Sub ConvNSN(N, M, P)
      P = 0
      If N < 0 Then
        S = -1
      ElseIf N > 0 Then
        S = 1
      Else
        M = 0
        Exit Sub
      End If
      M = Abs(N)
      If M >= 10 Then
        While M >= 10
          M = M / 10
          P = P + 1
        Wend
        M = M * S
        Exit Sub
      End If
      If M < 1 Then
        While M < 1
          M = M * 10
          P = P - 1
        Wend
        M = M * S
        Exit Sub
      End If
    End Sub
    

    Based on the comments, I re-wrote this my way, ignoring the structure from the OP.

    MsgBox NSN(-0.0000000000000000000123456789,4)
    MsgBox NSN(1234567890000000000000000000,4)
    
    Function NSN(Number, Accuracy)
      Exponent = 0
      If Number > 0 Then
        Sign = 1
      ElseIf Number < 0 Then
        Sign = -1
      Else
        NSN = 0
        Exit Function
      End If
      Number = Number * Sign
      If Number >= 10 Then
        While Number >= 10
          Number = Number / 10
          Exponent = Exponent + 1
        Wend
      ElseIf Number < 1 Then
        While Number < 1
          Number = Number * 10
          Exponent = Exponent - 1
        Wend
      End If
      Number = Round(Number, Accuracy)
      If Number = "10" Then
        Number = 1
        Exponent = Exponent + 1
      End If
      Number = Number * Sign
      If Exponent = 0 Then
        NSN = Number
      Else
        NSN = Number & "E" & Exponent
      End If
    End Function