4
\$\begingroup\$

I'm working to implement a custom string format. These are the float values to convert into a string:

1,02458
0,4145805
0,06231292
0,04362812
1,16458
1,172608
0,06011338
0,05324263
0,0653288
0,0285034
0,0992517
0,0861678

This is the output I'm getting:

1.02458f
.4145805f
6.231292E-02f
4.362812E-02f
1.16458f
1.172608f
6.011338E-02f
5.324263E-02f
.0653288f
.0285034f
.0992517f
.0861678f

The idea is, if the decimal places are greater than 8, use the scientific notation. If not, remove the left zeros. Now I'm using this code, but I would like to know if there's another more efficient way of doing this but always getting the same output:

Dim numericProvider As New NumberFormatInfo With {
    .NumberDecimalSeparator = "."
}

'Get the number of decimal places
Dim stringNum As String = waveDuration.ToString(numericProvider)
Dim decimalPlaces As Integer = 0
If InStr(1, stringNum, ".", CompareMethod.Binary) Then
    decimalPlaces = stringNum.Substring(stringNum.IndexOf(".")).Length
End If

'Format String
Dim waveDurationFormat As String
If decimalPlaces > 8 Then
    waveDurationFormat = waveDuration.ToString("0.######E+00", numericProvider)
Else
    waveDurationFormat = waveDuration.ToString(".#######", numericProvider)
End If

ListBox3.Items.Add(waveDurationFormat)
\$\endgroup\$
5
  • \$\begingroup\$ Not a code review, but of the algorithm you chose: wouldn't it make more sense to use scientific notation when there are many leading zeros, rather than many significant fractional digits? Or more generally, when the number of total digits is 2 or 3 greater than the number of significant digits, so you'd also scientific for 120000.0 => 1.2E5f. This would avoid lengthening numbers, and also choose the same format for two small numbers of the same order of magnitude. (e.g. 0.01 and 0.01234567 would both stay as-is, so they visually line up for humans. Not so for large numbers though.) \$\endgroup\$ Mar 16 at 18:55
  • \$\begingroup\$ Hello, yes, you're right. But the tool, I'm working on, is for a custom-made engine for a video game, and for some unknown reason uses this format. But yeah, it would make more sense of doing it with this other way. \$\endgroup\$
    – jms2505
    Mar 18 at 23:32
  • \$\begingroup\$ Does it not accept numbers like 1.2E5f, but does accept 1.2345E5f? Or it prints numbers that way and you're trying to exactly match its output? \$\endgroup\$ Mar 18 at 23:52
  • \$\begingroup\$ Well, with this code I've got the same output, the original exporter also prints the numbers in this way. \$\endgroup\$
    – jms2505
    Mar 22 at 9:16
  • \$\begingroup\$ Ok, so you are just trying to match its exact output for some reason. (Perhaps so text diffs work better). Not because something else would reject this input if you made saner choices. \$\endgroup\$ Mar 22 at 17:30

1 Answer 1

6
\$\begingroup\$

Welcome to Code Review! Nice first question.

Instead of using Instr() to check if stringNum contains a . you can just use String.IndexOf() and check if the result is > -1 which means the string representation of waveDuration contains a ..

If IndexOf() returns a value greater than -1 we can just check if stringNum.Length - stringNum.IndexOf(".") > 8. This has the advantage that the code won't create a new string each time it is called because we don't need to call Substring() anymore.

I would place this whole stuff inside an Extension method having an optional parameter stating the numbers of decimalplaces like so

Imports System.Globalization
Imports System.Runtime.CompilerServices
Module StringExtensions
    Dim numericProvider As New NumberFormatInfo With {.NumberDecimalSeparator = "."}

    <Extension()>
    Public Function ToCustomString(ByVal value As Single, Optional scientificNotationDecimalPlaces As Integer = 8) As String
        If scientificNotationDecimalPlaces < 0 Then
            Throw New ArgumentOutOfRangeException(NameOf(scientificNotationDecimalPlaces))
        End If

        Dim stringValue As String = value.ToString(numericProvider)

        Dim decimalPointIndex As Integer = stringValue.IndexOf(".")

        If decimalPointIndex > -1 AndAlso stringValue.Length - decimalPointIndex > scientificNotationDecimalPlaces Then
            Return value.ToString("0.######E+00", numericProvider)
        End If

        Return value.ToString(".#######", numericProvider)

    End Function
End Module  

You could then call it like so

ListBox3.Items.Add(waveDuration.ToCustomString())

or e.g if you want to have the sientific notation only if the decimal places are greater than 5 like so

ListBox3.Items.Add(waveDuration.ToCustomString(5))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.