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 Single
Double
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 Double
to , 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
, CInt
itd.) 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 True
False
. 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 Boolean
wartość , 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 Boolean
wartość . 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.