3. Operatoren und Ausdrücke

3. Operatoren und Ausdrücke
Motivation
Ein Ausdruck ist eine Folge aus Operanden und Operatoren und hat einen Wert
Beispiel:
1
1.0
1.0
+
+
+
2
2
2.0
Allgemein:
Operand 1 Operator Operand 2
„drei“
„drei“
„drei“
ergibt ein
Ergebnis
Fragestellungen:
Was passiert bei unterschiedlichen Typen von Operanden?
Welchen Typ erhält das Ergebnis?
Welche Rechenvorschrift verbirgt sich hinter dem Operator ?
In welcher Reihenfolge wird bei mehr als 2 Operanden ausgewertet (Vorrang)?
Wie kann man Typen ggf. umwandeln?
Dr. Norbert Spangler / Programmieren I
16.10.2015
2
3.1 Zuweisungsoperator
Der „Befehl“ Zuweisung:
i
=
1;
Variable
=
Ausdruck
L-Wert
Operand
Zuweisungsoperator
Operator
R-Wert
Operand
Ergebnis/Wert?
C++ ist so aufgebaut, dass Ausdrücke (also auch eine Zuweisung) immer einen
Wert haben, der auch abgefragt werden kann.
Hier wird der Wert des Ausdrucks rechts vom = Operator genommen, also 1
d.h. der Ausdruck i=1 hat den Wert 1, d.h. den R-Wert.
Dr. Norbert Spangler / Programmieren I
16.10.2015
3
Wirkungsweise
Variable
=
Ausdruck
L-Wert
=
R-Wert
Es wird zuerst der Wert des Ausdrucks berechnet (R-Wert), der i.d.R. in einem
internen Speicherplatz gespeichert wird. Dieser Wert überschreibt dann den
alten Inhalt der Variablen (L-Wert)
Beispiel:
loesung1=(-b+sqrt(b*b-4*a*c)/2;
loesung2=(-b-sqrt(b*b-4*a*c)/2;
double h=loesung1; //Vertauschen von loesung1 und loesung2
loesung1=loesung2;
loesung2=h;
i=j=5; // Vorrang:Auswertung von rechts nach links also i=(j=1);
Dr. Norbert Spangler / Programmieren I
16.10.2015
4
Zusammengesetzte Zuweisung
Anstelle von
Übliche Schreibweise
i=i+1;
Alternative in C++
i+=1;
d=d*(r+2);
d*=r+2;
summe=summe+summand;
summe+=summand;
Summation: neue Summe = alte Summe + Summand
Allgemein
v=v op r;
Beispiele
+=
Dr. Norbert Spangler / Programmieren I
-=
v op= r;
*=
/=
%=
16.10.2015
5
3.2 Binäre arithmetische Operatoren
+ - * / % (modulo für ganzzahlige Op.) für arithmetische Datentypen
Beispiele :
7+5 22-10
2.5*2.5*3.14159 23/10 23.0/10
7%2 ergibt 1
27%4 ergibt 3
In einem Ausdruck können
Funktionen (genauer Funktionswerte),
Variable,
Konstante und Literale auftreten
und natürlich auch Ausdrücke,
auch in Klammern.
sqrt(b*b-4*f(a)*(b-a))/(x+y)
Dies entspricht der üblichen mathematischen Schreibweise.
Dr. Norbert Spangler / Programmieren I
16.10.2015
6
Typ des Ergebnisses
In C++ werden immer Operanden eines Typs miteinander verknüpft. Das
Ergebnis ist dann vom selben Typ. Unterschiedliche Typen werden vorher intern
„passend“ umgewandelt
int op int
-> Ergebnistyp: int
Bsp. 3/2 ergibt 1 (!!!!)
double op double
-> Ergebnistyp: double
Bsp. 3.2/2.5 ergibt 1.28
int op double
-> double op double
-> Ergebnistyp: double
Bsp. 3/2. ergibt 1.5
Weitere Beispiele zur internen Umwandlung
short op int
->
int op int
float op double ->
double op double
usw.
Dr. Norbert Spangler / Programmieren I
16.10.2015
7
Vorrangregeln
Bei Ausdrücken wie a op b op c …gelten die üblichen Vorrangregeln
Punkt vor Strich
Klammern haben höhere Priorität
Bei gleichen Operatoren oft von links nach rechts
Beispiel :
7 + 5*3*4 – 7
5*3
15 * 4
7 + 60
67 – 7
ergibt 15
ergibt 60
ergibt 67
ergibt 60
Hinweis : Klammern verwenden !!!!!
Merke : a + b + c ist nicht immer dasselbe wie a + c + b
(Rundung !!!!)
Dr. Norbert Spangler / Programmieren I
16.10.2015
8
3.3 Unäre arithmetische Operatoren
Vorzeichenoperatoren
Wirkung
:
+ -
-a
Umkehrung des Vorzeichens
+n
Wert des Operanden (nicht benötigt)
Dr. Norbert Spangler / Programmieren I
16.10.2015
9
Inkrement/Dekrement
Inkrement-Operator
Dekrement-Operator
Wirkung
++
-++i
erhöhe i um 1
rechne/arbeite mit dem erhöhten Wert
i++
rechne/arbeite mit dem „alten“ Wert
erhöhe danach i um 1
--i
vermindere i um 1
rechne/arbeite mit dem verminderten Wert
i--
rechne/arbeite mit dem „alten“ Wert
vermindere danach i um 1
Merke : nicht auf Konstanten anwendbar
Dr. Norbert Spangler / Programmieren I
16.10.2015
10
Inkrement/Dekrement: Beispiel
#include <iostream>
using namespace std;
void main()
{
int i=0;
cout<<++i<<endl;
cout<<i++<<endl;
cout<<i<<endl;
cout<<--i<<endl;
cout<<i--<<endl;
cout<<i<<endl;
}
Dr. Norbert Spangler / Programmieren I
16.10.2015
11
Inkrement/Dekrement
Die Verwendung von ++ bzw. -- hat zur Konsequenz, dass während der
Auswertung eines (ev. sehr komplexen) Ausdrucks „nebenbei“ eine
Variable geändert wird -> Seiteneffekt (sollte dort vermieden werden)
-> Fehleranfällig
-> schwer lesbar/nachvollziehbar etwa bei j=(i++ - k--)*(++i - --k)
Verwendung in Schleifen ist „Standard“:
for (int i=0;i<n;i++)
->später
Erhöhung einer Variablen um 1:
schritte++; //anstelle von schritte=schritte+1 oder schritte+=1;
Dr. Norbert Spangler / Programmieren I
16.10.2015
12
3.4 Vergleichsoperatoren
Beispiel: a>0 Der Ergebnistyp ist bool !
<
<=
>
>=
==
!=
kleiner
kleiner gleich
größer
größer gleich
gleich Häufiger Fehler: man schreibt a=0
ungleich
arithmetische Operatoren haben Vorrang vor Vergleichsoperatoren
a–3 <b+1
Berechne a-3
Berechne b+1
Vergleiche
Lesbarer ist (a-3) < (b+1)
Dr. Norbert Spangler / Programmieren I
16.10.2015
13
Vergleichsoperatoren/Beispiel
#include <iostream>
using namespace std;
void main()
{
int i=0;
cout<<"i<1 "<<(i<1)<<endl;
cout<<"i>1 "<<(i>1)<<endl;
cout<<"i=1 "<<(i=1)<<endl;
}
Dr. Norbert Spangler / Programmieren I
!!!!!
16.10.2015
14
Vergleichsoperatoren/weitere Beispiele
char c, d;
c < d // c steht im ASCII-Code vor d
string s1,s2
s1 < s2 // s1 steht im Lexikon vor s2 (gemäß ASCII-Code)
bool b;
b==true // ungeschickt
b
// ist gleichwertig und besser
Noch besser: lesbare Namen für boolsche Variable wie :
istWahr – istKleiner – istBebaut
Dr. Norbert Spangler / Programmieren I
16.10.2015
15
3.5 Logische Operatoren
&&
||
!
Tabelle
a
true
true
false
false
und
oder
nicht
b
true
false
true
false
Dr. Norbert Spangler / Programmieren I
(binärer Operator)
(binärer Operator)
(unärer Operator)
a&&b
true
false
false
false
a||b
true
true
true
false
!a
false
false
true
true
16.10.2015
16
Logische Operatoren/Beispiele
double x;
0 < x && x <= 1 // x liegt im Intervall (0,1])
besser ( 0 < x ) && ( x <= 1 )
char c;
('a' <= c) && (c<='z') // c ist ein Kleinbuchstabe
('A' <= c) && (c<= 'Z') // c ist ein Großbuchstabe
('0' <= c) && (c<='9') // c ist eine Ziffer
bool b,c;
b && !c || c
Dr. Norbert Spangler / Programmieren I
16.10.2015
17
3.6 Typumwandlungen
Implizite Typumwandlungen (automatisch durch C++)
Konvertierung arithmetischer Operanden z.B. int -> double
Beispiel:
u=2*r*pi;
Explizite Typumwandlungen
Aufruf eines Cast-Operators
Anmerkung
Man sollte in der Regel einen Cast-Operator verwenden und
nicht die implizite Typumwandlung. Damit wird dokumentiert,
dass die Umwandlung vom Programmierer beabsichtigt ist
und kein Versehen.
Dr. Norbert Spangler / Programmieren I
16.10.2015
18
Implizite Typumwandlungen
Bei Rechnungen mit arithmetischen Datentypen findet eine
automatische Konvertierung statt. Beispiel :
#include <iostream>
using namespace std;
void main()
{
int i=1,j;
double r,s=2.3;
r=i;
j=s;
cout << i<<" "<<s<<endl;
cout <<showpoint<< r<<" "<<j<<endl;
}
->Eventuell Warnung wg. Informationsverlust
c:\test\main.cpp(9) : warning C4244: '=': Konvertierung von 'double' in
'int', möglicher Datenverlust
Dr. Norbert Spangler / Programmieren I
16.10.2015
19
Explizite Typumwandlungen/C-Cast
Durch Anwendung des Cast-Operators (typ) ist es möglich, explizit den Typ
eines Ausdrucks zu ändern :
(typ) Ausdruck // sogenannter C-Cast
Alternative Schreibweise
typ(Ausdruck) //sogenannter „Funktions-Cast“
Beispiel:
int i=5,j=2,k;
double y=2.5;
k=int(y) ;
y=(double)i/j;
y=double(i)/double(j) ;
y=(double)(j/i);
Dr. Norbert Spangler / Programmieren I
Ergebnis
k=2
y=2.5 / j wird implizit umgewandelt
y=2.5
y=0.0
16.10.2015
20
Explizite Typumwandlungen /C++-Cast
Schreibweisen:
static_cast<typ>(Ausdruck)
Beispiel:
int i=5,j=2,k;
double y=2.5;
k=static_cast<int>(y);
Ergebnis
k=2
y=static_cast<double>(i)/j;
y=2.5 / j wird implizit umgewandelt
y=static_cast<double>(i)/static_cast<double>(j);
y=2.5 / schwer lesbar
Anmerkung:
Diese Schreibweise orientiert sich an der üblichen für Cast-Operatoren in
C++ (es gibt noch weitere).
Dr. Norbert Spangler / Programmieren I
16.10.2015
21
3.7 sizeof-Operator
Der sizeof-Operator gibt die Anzahl der erforderlichen Bytes für
die Speicherung von Typen, Variablen, Konstanten bzw.
Ausdrücken an.
#include <iostream>
using namespace std;
void main()
{
cout <<"\n int \t\t "<< sizeof(int);
cout <<"\n int \t\t "<< sizeof(1+2*3);
cout <<"\n long \t\t "<< sizeof(long);
cout <<"\n char A \t "<< sizeof('A');
cout <<"\n Zeichenkette A "<< sizeof("A") << " !!!!";
cout <<"\n cin \t\t"<< sizeof(cin);
cout <<"\n cout \t\t" << sizeof(cout);
cout <<endl;
}
Dr. Norbert Spangler / Programmieren I
16.10.2015
22
3.8 Vorrangregeln
Wenn mehrere Operanden und Operatoren in einem
Ausdruck auftreten sind Klammern (höchste Priorität) und
Vorrangregeln (z.B. Punkt vor Strich) zu beachten, aber
auch, ob gleichrangige Operatoren von links nach rechts
oder umgekehrt ausgewertet werden.
Möglichst Klammern verwenden anstelle von Regeln
Komplexe Ausdrücke zerlegen mit Hilfsvariablen
cout<<“Ergebnis“<<-u+v*r-s<<endl;
besser
cout<<“Ergebnis“<< (-u+v*r-s) <<endl;
x1=(-(r+s)/3+sqrt((r+s)*(r+s)/9-4*(u-2*v)*c))/(2*(u-2*v));
besser
double a=u-2*v;
Siehe Tabellen
double b=(r+s)/3;
double dis=b*b-4*a*c;
x1 =( -b+sqrt(dis))/2/a;
Dr. Norbert Spangler / Programmieren I
16.10.2015
23
3.9 Kommentare
Kommentare werden durch besondere Zeichen eingeleitet:
1) Alles was nach den Zeichen // in einer Zeile steht
2) Alles was zwischen den Zeichen /* .... */ steht
Kommentare beeinflussen den Lauf eines Programms in keiner Weise.
großzügige Verwendung von Kommentaren
Was macht das Programm – der Programmabschnitt – der Befehl ?
Was wird eingelesen – was wird ausgegeben (Schnittstellen) ?
Welche Variablen werden verwendet (sofern keine sprechenden Namen) ?
Dr. Norbert Spangler / Programmieren I
16.10.2015
24
3.10 Escape-Sequenzen
Zeichen mit besonderer Bedeutung wie etwa " , sowie grafisch nicht
darstellbare Zeichen können nur mittels einer besonderen Technik
verwendet werden:
Escape-Sequenzen
Diese beginnt immer mit dem Zeichen \ (Backslash).
und wird in der Regel innerhalb von Zeichenketten speziell bei der
Ausgabe eingesetzt:
cout<< "\n"; gibt das Zeichen Neue Zeile aus
cout<< "\tabc"; positioniert abc auf der nächsten Tabulatorposition
cout<< "\""; gibt das Zeichen Doppelapostroph aus
cout<< "\\"; gibt das Zeichen Backslash aus
Dr. Norbert Spangler / Programmieren I
16.10.2015
25
Escape-Sequenzen
Einzelzeichen
Bedeutung
ASCII-Zeichen
ASCII-Code
\a
alert
BEL
07
\b
backspace
BS
08
\t
horizontal tab
HT
09
\n
line feed
LF
0A
\v
vertical tab
VT
0B
\f
form feed
FF
0C
\r
carriage return
CR
0D
\”
“
“
22
\‘
‘
‘
27
\?
?
?
3F
\\
\
\
5C
\0
Stringende-Zeichen
NUL
00
\ooo
(max. 3 Oktalziffern)
Numerischer Wert
eines Zeichens
ooo
\xhh
(Hexadezimalziffern)
Numerischer Wert
eines Zeichens
hh
Dr. Norbert Spangler / Programmieren I
16.10.2015
26
3.11 Sonstige Operatoren
Später:
*
Operator
->
Pfeiloperator
&
Adressoperator
#include <iostream>
using namespace std;
void main()
{
int i=1,j=2;
cout<<"i&&j "<<(i&&j)<<endl;
cout<<"i&j "<<(i&j)<<endl;
Bitoperatoren (eventuell später)
cout<<"i||j "<<(i||j)<<endl;
&
und
cout<<"i|j "<<(i|j)<<endl;
|
oder
}
^
exklusives oder
~
nicht
>>
Rechtsshift
<<
Linksshift
Dr. Norbert Spangler / Programmieren I
16.10.2015
27
3.12 Risiken der Arithmetik
Ganze Zahlen: Überlauf
0111000011110000 positiv
Ganze Zahlen werden „naiv“ addiert ohne + 0111000011110000 positiv
Berücksichtigung des Vorzeichenbits
= 1110000111100000 negativ
Sind positive Summanden zu groß kann das Ergebnis negativ werden !!!!
Gleitpunksubtraktion – Genauigkeitsverlust
z=0.00000798702
float x=0.321699, y=0.321691,z=y-x;
nur noch 2 Stellen genau !!!
wahres Ergebnis z=0.00000800000
x und y hatten 6 Dezimalstellen Genauigkeit – z nur noch 2 !!!!!
Die Subtraktion etwa gleich großer Zahlen führt zum Verlust der Genauigkeit.
Gleitpunktaddition – kleine + große Zahlen
es ist c = a
double a=1.0,b=1.0e-16,c=a+b;
a+b+b+b+b+b+b+b+b+b+b = a ( Auswertung von links nach rechts)
b+b+b+b+b+b+b+b+b+b+a >a
Die Addition unterschiedlich großer Zahlen führt zu Rechenfehlern.
Dr. Norbert Spangler / Programmieren I
16.10.2015
28
Risiken der Arithmetik/Beispielprogramm
void main()
{
//Ueberlauf bei ganzen Zahlen
short i=28912, j=i, k=i+j;
cout<<"Ueberlauf ganzer Zahlen"<<endl;
cout<<i<<" + "<<j<<" = "<<k<<endl;
//Genauigkeitsverlust bei Gleitkommazahlen
float x=0.321699,y=0.321691,z=y-x;
cout<<endl<<"Subtraktion gleich grosser Zahlen"<<endl;
cout<<y<<" - "<<x<<" = "<<z<<endl;
//Kleine+große Gleitkommazahlen
double a=1.0,b=1.0e-16,c=a+b;
cout<<endl<<"Addition kleiner und grosser Zahlen"<<endl;
cout<<" a = c "<<boolalpha<<(a==c)<<endl;
cout<<setw(18)<<setprecision(16)<<a+b+b+b+b+b+b+b+b+b+b<<endl;
cout<<setw(18)<<setprecision(16)<<b+b+b+b+b+b+b+b+b+b+a<<endl;
}
Dr. Norbert Spangler / Programmieren I
Genauere
Betrachtung
->
Numerische
Mathematikt
16.10.2015
29
Risiken der Arithmetik/numerische Probleme
Beispiel 1:
Quadratische Gleichung ax2+bx+c=0 mit a=0.01 b=0.06 c=0.09
Lösung: 2-fache Nullstelle -3
Diskriminante mit float : b2-4ac = -2.2E-10<0 -> keine reelle Lösung!!!
Beispiel 2 (RWTH Aachen):
Man berechne f(x) = 1 – x*( (x+1)/x – 1) für double x=1E-7
Lösung: es ist stets f(x)=0
Ergebnis: -2.2E-16
Beispiel 3( Uni Karlsruhe):
Berechne für x=192119201 und y=35675640 den Ausdruck
z=(1682xy4 + 3x3 + 29xy2 – 2 x5 +832)/107751
Korrekt ist z=1783
z mit long double: 7.1806E+20
Dr. Norbert Spangler / Programmieren I
16.10.2015
30