Rozwiązywanie problemów związanych z typami danych (Visual Basic)

Na tej stronie wymieniono niektóre typowe problemy, które mogą wystąpić podczas wykonywania operacji na typach danych wewnętrznych.

wyrażenia Floating-Point nie są porównywane jako równe

Podczas pracy z liczbami zmiennoprzecinkowami (pojedynczy typ danych i podwójny typ danych) pamiętaj, że są one przechowywane jako ułamki binarne. Oznacza to, że nie mogą zawierać dokładnej reprezentacji jakiejkolwiek ilości, która nie jest ułamkiem binarnym (w postaci k / (2 ^ n), gdzie k i n są liczbami całkowitymi). Na przykład wartości 0,5 (= 1/2) i 0,3125 (= 5/16) mogą być przechowywane jako dokładne wartości, natomiast wartości 0,2 (= 1/5) i 0,3 (= 3/10) mogą być jedynie przybliżeniami.

Z powodu tej nieprawdopodobności nie można polegać na dokładnych wynikach podczas wykonywania operacji na wartościach zmiennoprzecinkowych. W szczególności dwie wartości, które teoretycznie są równe, mogą mieć nieco inne reprezentacje.

Aby porównać ilości zmiennoprzecinkowe
1. Oblicz wartość bezwzględną ich różnicy przy użyciu Abs metody Math klasy w System przestrzeni nazw.
2. Określ akceptowalną maksymalną różnicę, tak aby można było rozważyć, że dwie ilości są równe dla celów praktycznych, jeśli ich różnica nie jest większa.
3. Porównaj wartość bezwzględną różnicy z akceptowalną różnicą.

W poniższym przykładzie pokazano zarówno niepoprawne, jak i poprawne porównanie dwóch Double wartości.

Dim oneThird As Double = 1.0 / 3.0
Dim pointThrees As Double = 0.333333333333333

' The following comparison does not indicate equality.
Dim exactlyEqual As Boolean = (oneThird = pointThrees)

' The following comparison indicates equality.
Dim closeEnough As Double = 0.000000000000001
Dim absoluteDifference As Double = Math.Abs(oneThird - pointThrees)
Dim practicallyEqual As Boolean = (absoluteDifference < closeEnough)

MsgBox("1.0 / 3.0 is represented as " & oneThird.ToString("G17") &
    vbCrLf & "0.333333333333333 is represented as " &
    pointThrees.ToString("G17") &
    vbCrLf & "Exact comparison generates " & CStr(exactlyEqual) &
    vbCrLf & "Acceptable difference comparison generates " &
    CStr(practicallyEqual))

W poprzednim przykładzie użyto ToString metody Double struktury, aby określić lepszą precyzję niż CStr używane przez słowo kluczowe. Wartość domyślna to 15 cyfr, ale format "G17" rozszerza go na 17 cyfr.

Operator mod nie zwraca dokładnego wyniku

Ze względu na niemożliwość przechowywania zmiennoprzecinkowego operator moda może zwrócić nieoczekiwany wynik, gdy co najmniej jeden z operandów jest zmiennoprzecinkowy.

Typ danych dziesiętnych nie używa reprezentacji zmiennoprzecinkowej. Wiele liczb, które są niedokładne i SingleDouble są dokładne w Decimal (na przykład 0.2 i 0.3). Mimo że arytmetyka jest wolniejsza niż Decimal w zmiennoprzecinku, warto zmniejszyć wydajność, aby osiągnąć lepszą precyzję.

Aby znaleźć pozostałą liczbę całkowitą ilości zmiennoprzecinkowych
1. Zadeklaruj zmienne jako Decimal.
2. Użyj znaku D typu literału, aby wymusić literały na Decimal, jeśli ich wartości są zbyt duże dla Long typu danych.

W poniższym przykładzie pokazano potencjalną nieprawdopodobną liczbę operandów zmiennoprzecinkowych.

Dim two As Double = 2.0
Dim zeroPointTwo As Double = 0.2
Dim quotient As Double = two / zeroPointTwo
Dim doubleRemainder As Double = two Mod zeroPointTwo

MsgBox("2.0 is represented as " & two.ToString("G17") &
    vbCrLf & "0.2 is represented as " & zeroPointTwo.ToString("G17") &
    vbCrLf & "2.0 / 0.2 generates " & quotient.ToString("G17") &
    vbCrLf & "2.0 Mod 0.2 generates " &
    doubleRemainder.ToString("G17"))

Dim decimalRemainder As Decimal = 2D Mod 0.2D
MsgBox("2.0D Mod 0.2D generates " & CStr(decimalRemainder))

W poprzednim przykładzie użyto ToString metody Double struktury, aby określić lepszą precyzję niż CStr używane przez słowo kluczowe. Wartość domyślna to 15 cyfr, ale format "G17" rozszerza go na 17 cyfr.

Ponieważ zeroPointTwo jest Doubleto , jego wartość dla 0,2 jest nieskończenie powtarzanym ułamkiem binarnym o wartości przechowywanej 0,200000000000001. Dzielenie 2,0 przez tę ilość daje 9,99999999999999999995 z resztą 0,199999999999999999999999991.

W wyrażeniu dla decimalRemainder, znak D typu literału wymusza zarówno operandy na Decimal, jak i 0,2 ma dokładną reprezentację. W związku z tym Mod operator daje oczekiwaną resztę 0,0.

Należy pamiętać, że nie wystarczy zadeklarować decimalRemainder jako Decimal. Należy również wymusić użycie literałów na Decimal, lub domyślnie Double używać tych decimalRemainder samych niedokładnych wartości co doubleRemainder.

Typ logiczny nie jest dokładnie konwertowany na typ liczbowy

Wartości typu danych logicznych nie są przechowywane jako liczby, a przechowywane wartości nie mają być równoważne liczbom. Aby zapewnić zgodność z wcześniejszymi wersjami, język Visual Basic udostępnia słowa kluczowe konwersji (funkcja CType, CBool, CIntitd.) w celu konwersji między typami Boolean liczbowymi i . Jednak inne języki czasami wykonują te konwersje inaczej, podobnie jak metody .NET Framework.

Nigdy nie należy pisać kodu, który opiera się na równoważnych wartościach liczbowych dla i TrueFalse. Jeśli to możliwe, należy ograniczyć użycie Boolean zmiennych do wartości logicznych, dla których zostały zaprojektowane. Jeśli musisz mieszać Boolean i wartości liczbowe, upewnij się, że rozumiesz wybraną metodę konwersji.

Konwersja w Visual Basic

Gdy używasz CType słów kluczowych konwersji lub CBool do konwertowania typów danych liczbowych na Booleanwartość , wartość 0 staje się False i wszystkie inne wartości stają się .True Gdy konwertujesz Boolean wartości na typy liczbowe przy użyciu słów kluczowych konwersji, False staje się 0 i True staje się -1.

Konwersja w strukturze

ToInt32 Metoda Convert klasy w System przestrzeni nazw jest konwertowana True na +1.

Jeśli musisz przekonwertować Boolean wartość na typ danych liczbowych, należy zachować ostrożność przy użyciu metody konwersji.

Literał znaku generuje błąd kompilatora

W przypadku braku znaków dowolnego typu język Visual Basic zakłada domyślne typy danych literałów. Domyślnym typem literału znaku — ujętego w cudzysłów (" ") — jest String.

Typ String danych nie rozszerza się na typ danych char. Oznacza to, że jeśli chcesz przypisać literał do Char zmiennej, musisz dokonać konwersji zawężającej lub wymusić literał na Char typ.

Aby utworzyć literał char do przypisania do zmiennej lub stałej
1. Zadeklaruj zmienną lub stałą jako Char.
2. Ujmij wartość znaku w cudzysłów (" ").
3. Postępuj zgodnie z zamykającym podwójnym cudzysłowem z znakiem C typu literału, aby wymusić literał na Char. Jest to konieczne, jeśli przełącznik sprawdzania typów (Instrukcja ścisłej opcji) jest On, i jest to pożądane w każdym przypadku.

W poniższym przykładzie pokazano zarówno nieudane, jak i pomyślne przypisania literału do zmiennej Char .

Dim charVar As Char
' The following statement attempts to convert a String literal to Char.
' Because Option Strict is On, it generates a compiler error.
charVar = "Z"
' The following statement succeeds because it specifies a Char literal.
charVar = "Z"c
' The following statement succeeds because it converts String to Char.
charVar = CChar("Z")

Zawsze istnieje ryzyko użycia konwersji zawężania, ponieważ mogą zakończyć się niepowodzeniem w czasie wykonywania. Na przykład konwersja z String na Char może zakończyć się niepowodzeniem, jeśli String wartość zawiera więcej niż jeden znak. W związku z tym lepszym programowaniem C jest użycie znaku typu.

Konwersja ciągów kończy się niepowodzeniem w czasie wykonywania

Typ danych ciągów uczestniczy w bardzo niewielu konwersji rozszerzających. String rozszerzenie tylko do siebie i Object, i tylko Char i Char() (tablica Char ) poszerzyć do String. Dzieje się tak, ponieważ String zmienne i stałe mogą zawierać wartości, których nie mogą zawierać inne typy danych.

Gdy przełącznik sprawdzania typów (Instrukcja ścisłej opcji) to On, kompilator nie zezwala na wszystkie niejawne konwersje zawężające. Obejmuje to osoby z udziałem String. Kod nadal może używać słów kluczowych konwersji, takich jak CStr i CType, które kierują .NET Framework do próby konwersji.

Uwaga

Błąd konwersji zawężającej jest pomijany dla konwersji z elementów w For Each…Next kolekcji do zmiennej sterującej pętli. Aby uzyskać więcej informacji i przykłady, zobacz sekcję "Zawężanie konwersji" w temacie For Each... Następna instrukcja.

Ochrona konwersji zawężającej

Wadą zawężania konwersji jest to, że mogą one zakończyć się niepowodzeniem w czasie wykonywania. Jeśli na przykład zmienna String zawiera coś innego niż "Prawda" lub "Fałsz", nie można przekonwertować jej na Booleanwartość . Jeśli zawiera znaki interpunkcyjne, konwersja na dowolny typ liczbowy kończy się niepowodzeniem. Jeśli nie wiesz, że String zmienna zawsze przechowuje wartości, które typ docelowy może zaakceptować, nie należy próbować konwersji.

Jeśli musisz przeprowadzić konwersję z String na inny typ danych, najbezpieczniejszą procedurą jest ujęcie próby konwersji w try... Złapać... Finally, instrukcja. Umożliwia to radzenie sobie z błędem czasu wykonywania.

Tablice znaków

Pojedyncza Char i tablica Char elementów rozszerzają się do String. String Nie rozszerza jednak wartości Char(). Aby przekonwertować String wartość na tablicę Char , możesz użyć ToCharArray metody System.String klasy .

Wartości bez znaczenia

Ogólnie rzecz biorąc, String wartości nie są istotne w innych typach danych, a konwersja jest wysoce sztuczna i niebezpieczna. Jeśli to możliwe, należy ograniczyć użycie String zmiennych do sekwencji znaków, dla których zostały zaprojektowane. Nigdy nie należy pisać kodu, który opiera się na równoważnych wartościach w innych typach.

Zobacz też