Hallo, warum hat die Funktion abs() "int" als Rückgabetyp? https://github.com/bminor/glibc/blob/master/stdlib/abs.c http://git.musl-libc.org/cgit/musl/tree/src/stdlib/abs.c Das würde doch bei INT_MIN einen falschen Wert liefern. Weil INT_MAX ja um 1 kleiner als |INT_MIN| (Betrag) ist. Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht?
Nicht unbedingt: in den meisten realen Anwendungen willst du das Ergebnis ja weiterverarbeiten. Addieren, multiplizieren, vergleichen. Und da nützt dir ein unsigned dazwischen meist nichts.
zitter_ned_aso schrieb: > Das würde doch bei INT_MIN einen falschen Wert liefern. Weil INT_MAX ja > um 1 kleiner als |INT_MIN| (Betrag) ist. Zumindest beim Zweierkomplement. Daher ist auf solchen Systemen abs(INT_MIN) nicht definiert. Bei Systemen mit Einerkomplement ist es kein Problem, da dort abs(INT_MAX) == abs(INT_MIN) == INT_MAX ist. > Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht? Das ist so eine Sache. abs() ist schon ziemlich lange in C. Meines Wissens war es schon in der ersten Version des ANSI-C Standards im Jahr 1989. Schon damals war bekannt dass abs(INT_MIN) ein Problem sein kann. Das Verhalten nach 30 Jahren zu ändern? Wer weiß wie viel vorhandener Code dann auseinander fliegt.
zitter_ned_aso schrieb: > warum hat die Funktion abs() "int" als Rückgabetyp? Weil in den meisten Programmiersprachen, C eingeschlossen, der Datentyp nichts mit dem Zahlenart im mathematischen Sinne zu tun hat.
Michael Gugelhupf schrieb: > Daher ist auf solchen Systemen abs(INT_MIN) nicht definiert. Damit kann man übrigens schöne vulnerabilities bauen. Haben wir im Studium als Übungsaufgabe bekommen (vuln finden und exploiten). > Das Verhalten nach 30 Jahren zu ändern? Wer weiß wie viel vorhandener > Code dann auseinander fliegt. Korrekt. Eigentlich muss man vor jeder Verwendung einer Standard-C-Funktion die zugehörige manpage lesen und sich solcher und ähnlicher Fallstricke bewusst sein. man 3 abs: > NOTES > Trying to take the absolute value of the most negative > integer is not defined.
ichWillNurRaus schrieb: > Und da nützt dir ein unsigned dazwischen meist nichts. Schlimmer noch: UNsigned ist ANsteckend, so die Eselsbrücke. Da würde ansonsten der ganze Ausdruck mit unsigned gerechnet.
Bastler schrieb: > zitter_ned_aso schrieb: >> warum hat die Funktion abs() "int" als Rückgabetyp? > > Weil in den meisten Programmiersprachen, C eingeschlossen, der Datentyp > nichts mit dem Zahlenart im mathematischen Sinne zu tun hat. Aber eigentlich wählt man den Datentyp geschickterweise so, dass er den nötigen Wertebereich auch voll unterstützt. Das ist hier nicht gegeben, da abs(INT_MAX) eben beim Zweierkomplement auf Grund des Rückgabetyps nicht darstellbar ist. Jens schrieb: > Eigentlich muss man vor jeder Verwendung einer Standard-C-Funktion die > zugehörige manpage lesen und sich solcher und ähnlicher Fallstricke bewusst > sein. Nicht unbedingt vor jeder Verwendung, denn diese Fallstricke ändern sich ja nicht dauernd. Aber man sollte für jede Funktion wissen, welche potenziellen Probleme es damit geben kann und diese entsprechend berücksichtigen. Wenn man eine Funktion nur alle 5 Jahre mal benutzt, ist es aber natürlich besser, jedes mal nachzuschauen.
zitter_ned_aso schrieb: > warum hat die Funktion abs() "int" als Rückgabetyp? > Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht? Aus dem gleiche Grund könntest du fragen, warum die Addition und Multiplikation zweier int-Werte wieder ein int und nicht ein long ergeben. In C sind alle arithmetischen Datentypen mit einer Größe von mindestens int bzgl. aller darauf definierter Rechenoperationen abgeschlossen (bzw. für bestimmte Operanden undefiniert). Da wäre es komisch, wenn abs eine Ausnahme machen würde.
:
Bearbeitet durch Moderator
Yalu X. schrieb: > Aus dem gleiche Grund könntest du fragen, warum die Addition und > Multiplikation zweier int-Werte wieder ein int und nicht ein long > ergeben. Gerade bei der Multiplikation wäre das durchaus sehr praktisch gewesen. Auf Assembler-Ebene passiert meistens genau das: Das Ergebnis ist doppelt so breit wie die Operanden. Aber da C das nicht auch so macht, muss man Multiplikation dort öfter mal mit dem doppelten der eigentlich benötigten Bitbreite durchführen, damit nicht die Hälfte des Ergebnisses verworfen wird.
1 | void func(int16_t a, int16_t b) |
2 | {
|
3 | // Rechnung als 16 Bit * 16 Bit, Ergebnis wird abgeschnitten
|
4 | int32_t result = a * b; |
5 | |
6 | // korrektes Ergebnis, aber Multiplikation muss (meist unnötigerweise) in
|
7 | // 32 Bit durchgeführt werden.
|
8 | int32_t result = (int32_t)a * (int32_t)b; |
9 | }
|
Rolf M. schrieb: > // Rechnung als 16 Bit * 16 Bit, Ergebnis wird abgeschnitten > int32_t result = a * b; Das kommt drauf an, was auf der jeweiligen Plattform denn die Breite von int ist - Stichwort "integer promotion". Wenn int als 32 bit ist, wird da nichts abgeschnitten.
Rolf M. schrieb: > Aber da C das nicht auch so macht, > muss man Multiplikation dort öfter mal mit dem doppelten der eigentlich > benötigten Bitbreite durchführen, damit nicht die Hälfte des Ergebnisses > verworfen wird. Dass ist dann der Moment wo du hoffst, dass der Optimizer erkennt was wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit Multiplikation einfügt.
Michael Gugelhupf schrieb: > dass der Optimizer erkennt was > wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit > Multiplikation einfügt. Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner magischen Erweiterung von 16 auf 32 Bit. Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und Typ-Balancierung. Diese sind sehr genau definiert und keineswegs zufällig von irgendeiner Implementierung des Optimizers abhängig.
MaWi N. schrieb: > Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner > magischen Erweiterung von 16 auf 32 Bit. > Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und > Typ-Balancierung. Diese sind sehr genau definiert und keineswegs > zufällig von irgendeiner Implementierung des Optimizers abhängig. Das ist jetzt auch wieder so eine Antwort die du nur geschrieben hast um irgend was zu sagen? Im C Standard ist klar geregelt das der Compiler optimieren darf wenn das Ergebnis genau so aussieht wie wenn er die vorgegebenen Abläufe wörtlich ausführen würde.
MaWi N. schrieb: > Michael Gugelhupf schrieb: >> dass der Optimizer erkennt was >> wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit >> Multiplikation einfügt. > > Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner > magischen Erweiterung von 16 auf 32 Bit. Doch, hat es. Du hast das Problem nur noch nicht verstanden. > Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und > Typ-Balancierung. Diese sind sehr genau definiert und keineswegs > zufällig von irgendeiner Implementierung des Optimizers abhängig. Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int habe und den mit einem zweiten int multipliziere, wird eine 16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch auf 16 Bit begrenzt, selbst wenn der Prozessor eine Instruktion hat, die daraus ein 32-Bit-Ergebnis produziert. Dann wirft der Compiler einfach die obere Hälfte des Ergebnisses weg. Will ich ein 32-Bit-Ergebnis, muss ich mindestens einen der Operanden auf 32 Bit casten, obwohl ich nur eine 16x16-Bit-Multikplikation brauche. Und dann muss ich hoffen, dass der Compiler merkt, dass der Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet wird, damit ich auch ein 32-bit-Ergebnis bekomme.
Rolf M. schrieb: > Aber eigentlich wählt man den Datentyp geschickterweise so, dass er den > nötigen Wertebereich auch voll unterstützt. Ein frommer Wunsch, der eine Sprache wie C zur praktischen Nicht-Nutzbarkeit degradieren würde. Selbst bei einer einfachen Addition müsste der Ergebnis-Datentyp um mindestens 1-Bit größer sein, als der größte der beiden Operanden. Der Denkfehler ist, ein C-Datentyp wie "int" wäre identisch zum Zahlenbereich der ganzen Zahlen mit ihren Gesetzen.
Rolf M. schrieb: > Und dann muss ich hoffen, dass der Compiler merkt, dass der > Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet > wird, damit ich auch ein 32-bit-Ergebnis bekomme. Die Vorstellung, die Definition einer Programmiersprache wie C hat etwas mit einer konkreten Implementierung für einen spezifischen Prozessor zu tun, ist infantil. Und ja, wenn Du in der Sprache C ein 32-Bit Ergebnis einer 16x16-Multiplikation haben willst, musst Du vorher casten. Genau so funktioniert es eben in "C". Wie das der Compiler in Maschinensprache umsetzt, ist in C eben nicht definiert.
Rolf M. schrieb: > Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int > habe und den mit einem zweiten int multipliziere, wird eine > 16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch > auf 16 Bit begrenzt Hast Du Beitrag "Re: glibc: abs() hat int als Rückgabetyp. Warum?" nicht gelesen oder nicht verstanden?
Beitrag #6312428 wurde vom Autor gelöscht.
Bastler schrieb: > Rolf M. schrieb: >> Und dann muss ich hoffen, dass der Compiler merkt, dass der >> Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet >> wird, damit ich auch ein 32-bit-Ergebnis bekomme. > > Die Vorstellung, die Definition einer Programmiersprache wie C hat etwas > mit einer konkreten Implementierung für einen spezifischen Prozessor zu > tun, ist infantil. Allerdings ist es genauso "infantil" zu glauben, dass Programmiersprachen, zumindest solche eher hardwarenahen wie C, völlig losgelöst von jeglichen Hardware-Eigenschaften der üblichen Architekturen entwickelt werden. Denn am Ende soll die Sache ja auch performant sein. > Und ja, wenn Du in der Sprache C ein 32-Bit Ergebnis einer > 16x16-Multiplikation haben willst, musst Du vorher casten. Genau so > funktioniert es eben in "C". Ist das deiner Erfahrung nach auch die Art, wie es die überwiegende Zahl der CPU-Architekturen umsetzt? Die, die ich kenne, machen es alle nicht so. > Wie das der Compiler in Maschinensprache umsetzt, ist in C eben nicht > definiert.
Rolf M. schrieb: > Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int > habe und den mit einem zweiten int multipliziere, wird eine > 16-Bit-Multiplikation durchgeführt Nein. Das ist so pauschal gesagt nicht richtig. Typ-Promotion.
Rolf M. schrieb: > Wenn ich aber einen 16 Bit breiten int > habe und den mit einem zweiten int multipliziere, wird eine > 16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch > auf 16 Bit begrenzt, selbst wenn der Prozessor eine Instruktion hat, die > daraus ein 32-Bit-Ergebnis produziert. Dann wirft der Compiler einfach > die obere Hälfte des Ergebnisses weg. Falsch.
1 | $ cat t.c |
2 | #include <stdio.h> |
3 | #include <stdint.h> |
4 | int main(void) |
5 | {
|
6 | int16_t a = 30000; |
7 | int16_t b = 30000; |
8 | int32_t c = a * b; |
9 | printf("%d\n", c); |
10 | }
|
11 | $ gcc -o t t.c |
12 | $ ./t |
13 | 900000000
|
@MaWi: Ihr redet aneinander vorbei. Rolf schrieb von einem 16 Bit breiten int, du hingegen verwendest auf einem System mit 32 Bit breitem int ein int16_t. Auf diesem System ist int16_t eben etwas anderes als int.
Yalu X. schrieb: > @MaWi: Ihr redet aneinander vorbei. > > Rolf schrieb von einem 16 Bit breiten int, du hingegen verwendest auf > einem System mit 32 Bit breitem int ein int16_t. Auf diesem System ist > int16_t eben etwas anderes als int. Ja genau. Hiermit: Rolf M. schrieb: > Wenn ich aber einen 16 Bit breiten int habe und den mit einem zweiten int > multipliziere, wird eine 16-Bit-Multiplikation durchgeführt meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen, sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei dem der Datentyp "int" 16 Bit breit ist. War vielleicht etwas missverständlich formuliert.
Alles klar. Das war ein Missverständnis. Vorschlag für die Zukunft: "wenn ich einen 16 bit breiten int habe" -> "Wenn int 16 bit breit ist" Außerdem steht im Titel des Threads "glibc". Soweit ich weiß, unterstützt glibc keine Microcontroller mit 16 bit int.
Rolf M. schrieb: > meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen, > sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei > dem der Datentyp "int" 16 Bit breit ist. Das ist in der Realität nur auf zwei Sorten von CPUs der Fall: 1) 8-Bitter, weil int mindestens 16 bit breit ist, aber da hast Du sowieso keine 16-bit-Multiplikation in Hardware und folglich auch kein 32-bit-Ergebnis in Registern. 2) 16-Bitter, weil das der Registerbreite entspricht, und der letzte wirklich verbreitete war der 286er. Da war das tatsächlich so, daß das Ergebnis der Multiplikation von 16-bit-Operanden im Doppelregister DX:AX vorlag. Allerdings konnte man damit sowieso nicht direkt weiterrechnen, weil z.B. Addition nicht mit Doppelregistern ging. Aber auch wenn man das auf einem 16-bitter mit dem manuellen Operanden-Cast nach int32_t macht, heißt das keineswegs, daß die Multiplikation selber mit 32-bit-Operanden tatsächlich so durchgeführt wird. C hat nämlich die "as-if-Regel". Der Compiler darf machen, was er will, solange nur das Ergebnis identisch ist. Das bedeutet, daß er auf einem 286er das hier:
1 | int a = b = 300; |
2 | int32_t c = ((int32_t a) * ((int32_t b)); |
Sehr wohl optimieren darf zu genau dem, was ein 286er in Hardware kann. Also die Multiplikation eben nicht mit 32-bit-Operanden ausführen, sondern mit 16 bit, und nur das 32-bittige Ergebnis aus DX:AX in die Variable c schieben. Wenn der Compiler das nicht tut, ist es ein schlechter Compiler.
Nop schrieb: > 2) 16-Bitter, weil das der Registerbreite entspricht, > und der letzte wirklich verbreitete war der 286er. Das könnte TI eventuell etwas anders sehen. Der MSP430 wird in zahlreichen Varianten seit 27 Jahren verkauft.
hmmm, mit unsigned int kommt man also auch nicht weiter. Dann muss es so bleiben wie es ist ;-) Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp arbeitet, der gar nicht in der Lage ist alles darzustellen, was aus Funktionsparametern berechnet werden kann. Klar, auch Rechenoperationen können zu diesem Problem führen, aber das sind ja keine Funktionen. Das ist für mich schon was anderes. Es gibt unendlich viele Zahlenn - es ist egal mit welchen Datentypen man rechnet. Irgendwann wird man sowieso an die Grenzen stoßen. Eine Funktion schränkt aber durch die Parameterliste den Zahlenberech ein. Hier wird ja nur das Vorzeichen geändert (wenn überhaupt). Das ist nicht sowas wie: pow(INT_MAX, pow(INT_MAX, INT_MAX)) usw..
zitter_ned_aso schrieb: > Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp > arbeitet, der gar nicht in der Lage ist alles darzustellen, was aus > Funktionsparametern berechnet werden kann. Wie bereits gesagt, das ist historisch bedingt. Die nicht-signed-int-Typen sind erst nach und nach zum Sprachumfang hinzugekommen.
zitter_ned_aso schrieb: > Klar, auch Rechenoperationen können zu diesem Problem führen, aber das > sind ja keine Funktionen. Natürlich sind das Funktionen. Sie unterscheiden sich von "gewöhnlichen" Funktionen nur syntaktisch: - Statt aus Buchstaben und Ziffern besteht ihr Name aus Sonderzeichen. - Ihr Aufruf erfolgt in Infix- statt in Präfixnotation.
> Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp > arbeitet, der gar nicht in der Lage ist alles darzustellen, Ein Fall geht schief, der Rest läuft besser. Nop hat es oben schon gesagt: unsigned ist ansteckend und versteckte Typenwechsel mitten in einem Ausdruck sind häßlich: "abs(3) > -1" - true or false? Und "-3 * abs(3) < 0"?
Nop schrieb: > Rolf M. schrieb: > >> meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen, >> sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei >> dem der Datentyp "int" 16 Bit breit ist. > > Das ist in der Realität nur auf zwei Sorten von CPUs der Fall: Dann nimm halt eine Multiplikation zweier 32-bit-Integer zu einem mit 64 Bit. Gleiches Problem, aber auch auf 32-Bit-Plattformen relevant. > C hat nämlich die "as-if-Regel". Der Compiler darf machen, was er will, > solange nur das Ergebnis identisch ist. Das bedeutet, daß er auf einem > 286er das hier: > int a = b = 300; > int32_t c = ((int32_t a) * ((int32_t b)); > Sehr wohl optimieren darf zu genau dem, was ein 286er in Hardware kann. Ja, natürlich. Hab ich ja auch schon geschrieben, und "Michael Gugelhupf" vor mir. Das hat keiner bestritten. Yalu X. schrieb: > zitter_ned_aso schrieb: >> Klar, auch Rechenoperationen können zu diesem Problem führen, aber das >> sind ja keine Funktionen. > > Natürlich sind das Funktionen. Sie unterscheiden sich von "gewöhnlichen" > Funktionen nur syntaktisch: Das stimmt nur in C++ für benutzerdefinierte Operatoren.
Rolf M. schrieb: > Das stimmt nur in C++ für benutzerdefinierte Operatoren. Nein, Du siehst Unterschiede wo keine sind. Ja, in C kann man Operatoren nicht neu definieren. Das ist aber nicht das Thema. Ein Operator ist nur eine Funktion in einer anderen Schreibweise. Ob Du nun "add(a,b)" oder "a + b" schreibst, ist per se egal. Beides sind Funktionsaufrufe, die (mutmaßlich) zwei Parameter addieren. Die Sprache C stellt einen festen Vorrat an Operatoren zur Verfügung, da kannst Du als Programmierer nix ändern. In C++ kannst Du immerhin existierenden Operatoren überladen. In anderen Sprachen kannst Du auch neue Operatoren definieren. In wiederum anderen Sprachen kannst Du jede Binäre Funktion in Operatorschreibweise benutzen. Unterm Strich bleibt: Ein Operator ist nichts anderes als eine Funktion in anderer Schreibweise, also nur ein Unterschied im Syntax.
foobar schrieb: > Ein Fall geht schief, der Rest läuft besser. aber eigentlich könnte man ja sagen, dass das kein Problem von C ist. Die Zweierkomplement-Darstellung hat im negativen Bereich ein Zahl mehr. Hätten wir hier eine Symmetrie, so gebe es dieses Problem nicht. Oder wenn wir eine Zahl mehr im positiven Bereich hätten.
Bastler schrieb: > Ein Operator ist nur eine Funktion in einer anderen Schreibweise. Nein. Für einen Operator muss ich keinen Header mit einer Deklaration einbinden, um ihn zu nutzen. Ich kann den selben Operator auch mit mehreren verschiedenen Typen verwenden, obwohl C keine Funktionsüberladung kennt. Und es gibt auch nicht die gleichen Einschränkungen wie für den Aufruf von Funktionen, wie z.B. dass man sie nicht außerhalb von Funktionen aufrufen kann.
1 | #include <math.h> |
2 | |
3 | int x = 3 * 5; // Erlaubt, weil das Ergebnis eine Compilezeit-Konstante ist |
4 | double y = sin(M_PI); // Fehler: Funktionaufrufe sind an der Stelle nicht erlaubt |
5 | |
6 | int main() |
7 | {
|
8 | }
|
Der überwiegende Teil der Operatoren stellt auch keinen Sequenzpunkt dar im Gegensatz zu Funktionen. Es gibt in C ziemlich viele Unterschiede zwischen Operatoren und Funktionen. > In C++ kannst Du immerhin existierenden Operatoren überladen. Ja, und das sind dann wie gesagt auch tatsächlich Funktionen, für die die gleichen Regeln gelten wie für andere Funktionen auch. > In anderen Sprachen kannst Du auch neue Operatoren definieren. In wiederum > anderen Sprachen kannst Du jede Binäre Funktion in Operatorschreibweise > benutzen. Mag sein, dass es Sprachen gibt, in denen es keinerlei Unterschiede zwischen Operatoren und Funktionen gibt. C gehört aber nicht dazu.
Entschuldigung, ich wollte mit der Verwendung des Begriffs "Funktion" keine Verwirrung stiften :) Als ich oben "Funktion" schrieb, meinte ich damit keine C-Funktion, sondern eine Funktion im mathematischen Sinn, also etwas, was einem oder mehreren Argumenten ein Ergebnis zuordnet. Ich dachte, das würde aus dem Kontext klar hervorgehen, hätte mich aber wohl dennoch etwas klarer ausdrücken sollen. In meinem ersten beitrag wollte ich dem TE klar machen, dass die von ihm kritisierte Einengung des Wertebereichs von abs nichts Besonderes ist, weil es diese Einengung in ähnlicher Weise auch bei Additionen und Multiplikation gibt. Daraufhin entgegnete er: zitter_ned_aso schrieb: > Klar, auch Rechenoperationen können zu diesem Problem führen, aber das > sind ja keine Funktionen. Er akzeptiert das Problem also bei der Addition und Multiplikation, nicht aber bei der Absolutwertberechnung, und begründet dies damit dass die beiden ersteren keine Funktionen seien. Mit meiner Antwort Yalu X. schrieb: > Natürlich sind das Funktionen. > ... wollte ich deutlich machen, dass die Berechnung einer Summe oder eines Produkts und die Berechnung des Absolutwerts nicht so unterschiedlich sind, dass daraus die Forderung abgeleitet werden könnte, dass die abs-Funktion für gesamten Argumentwertebereich definiert sein muss. Addition und Multiplikation sind natürlich keine Functions im Sinne der Sprachspezifikation von C (das können sie schon allein deswegen nicht sein, weil ihr Name kein gültiger C-Identifier ist). Es sind aber sehr wohl Funktionen in dem Sinne, dass sie aus einem oder mehreren (im konkreten Fall zwei) Zahlenwerten einen Ergebniswert berechnen. Dass sie sich von C-Funktionen in einigen Punkten (wie bspw. Sequenzpunkten) unterscheiden, ist bei der Betrachtung der eigentlichen Berechnung und der Wertebereiche (um die es in diesem Thread ja ausschließlich geht) IMHO völlig irrelevant.
abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen Funktionswert abs(INT_MIN) gibt.
zitter_ned_aso schrieb: > abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im > Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen > Funktionswert abs(INT_MIN) gibt. Das ist jetzt aber ein schräger Schluss :) Andersherum wird ein Schuh daraus: Da der Funktionswert von INT_MIN nicht definiert ist, gehört INT_MIN nicht zum Definitionsbereich.
zitter_ned_aso schrieb: > abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im > Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen > Funktionswert abs(INT_MIN) gibt. So was nennt man glaub "partielle Funktion". Also eine Funktion die nicht für alle Eingangswerte ein Ergebnis liefert bzw. definiert ist. Eine "totale Funktion" wäre dann für alle denkbaren Eingangswerte definiert.
Rolf M. schrieb: > Es gibt in C ziemlich viele Unterschiede > zwischen Operatoren und Funktionen. Naja, die Unterschiede sind doch aber nicht von fundamentaler Natur. Bei einem guten Glas Wein betrachtet sind Operatoren nichts anderes als syntaktischer Zucker für Programmierer. Ohne Operatoren hätten wir vielleicht:
1 | #include <stdadd.h> |
2 | |
3 | int beispiel(int a, int b, int c) |
4 | {
|
5 | return addint(addint(a, b), c); |
6 | }
|
Mit Operatoren ist's halt bequemer:
1 | int beispiel(int a, int b, int c) |
2 | {
|
3 | return a + b + c; |
4 | }
|
Da ist kein großer Unterschied dazwischen.
Rolf M. schrieb: > Auf Assembler-Ebene passiert meistens genau das: Das Ergebnis ist > doppelt so breit wie die Operanden. Vor Äonen war das so. Bei einem gepipelinenten Multiplizierer ist es jedoch sinnvoller, zweimal zu multiplizieren, einmal für die untere und einmal für die obere Hälfte, als beides zusammen zu rechnen. Grund ist das Ressourcenmanagement von Bussen und Registern. Das war schon bei PowerPC so, anders als in IBMs POWER. Und die meistgebrauchten Multiplikationsbefehle bei x86 liefern nur die untere Hälfte. 32bit ARM hatte anfangs überhaupt nur diese Variante.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.