Kleines WTF aus einem internen Projekt
-
WTF.
-
Ah, ok, hab lexical_cast nochmal nachgeschaut. Ok, das ist natürlich wirklich böse
-
Und du hast schon Recht, das funktioniert, nur trotzdem, eieiei...
-
Ich frage mich gerade was der Compiler mit dem expliziten Cast nach float macht, oder ob er einfach direkt nach double umwandelt?
-
Jo das is alles ... boah.
In der einen Zeile ist einfach so viel daneben, das ist schon ein gutes Stück.
Ich hab' zwar schon Schlimmeres gesehen, aber nicht viel.
-
was soll da passieren?
-
daki schrieb:
was soll da passieren?
Im Endeffekt soll 2^(a+b) berechnet werden.
Ein kleiner Fail ist dass er dazu pow nimmt, obwohl man 2er Potenzen durch linksshiften deutlich performanter berechnen kann. Aber nur ein kleiner Fail, da pow mit hoher warscheinlichkeit rausoptimiert wird, dh. der Compiler schon shiftet.Der größere Fail ist dass der Compiler darüber jammert, dass aus einem double ein int gemacht wird und der falsche Cast verwendet wird. Obwohl das auch vermutlich hinfällig ist, bin mir recht sicher dass solche merkwürdigen Konvertierungen überladen sind und lexical_cast das nicht in einen stringstream schickt. Ansonsten würde lexical_cast nämlich vermutlich eine Exception werfen (bei der Umwandlung nach int wird nicht der ganze Stream konsumiert (Alles ab dem Punkt bleibt im Buffer, das ist für lexical_cast ein Fehlerfall)).
Also wohl eher ein minor Fail, wenn mich Compiler und Boost nicht enttäuschen.
-
arg.. ok.. hab da zuviel reininterpretiert.. hab aufgrund der char ... gemutmaßt, dass da noch irgendwie mit strings rumhantiert wird.
aber was kann einem dazu verführen hier nen lexical cast zu machen?
-
Also ich würde wetten dass
pow()
nicht rausoptimiert wird, und dass es an der Stelle kracht wennpow()
mal was ausspucken sollte was keine Ganzzahl ist.
Weillexical_cast
eben garantiert eine Exception zu werfen, wenn der Stream-Extraction Operator nicht alle Characters des Streams konsumiert. Ich gehe sogar davon aus dass hier auch wirklich der Umweg über den Stream gemacht wird. Mal sehen, vielleicht steppe ich einfach mal im Debugger rein und gucke was er macht.Weiters ist
pow(2.0, static_cast<float>(...))
einfach nur doof, weil es keine Funktionpow(double, float)
gibt. Es wird also sowiesopow(double, double)
verwendet. Also wenn schon unbedingt mitfloat
als Exponent, dann gleichpow(2.0f, static_cast<float>(...))
.
Und wozu überhaupt den Exponenten zu nemfloat
bzw.double
machen - schliesslich gibt espow(double, int)
.Und dann der
lexical_cast
. OMFG. Wenn ich nen positivenfloat
/double
habe, und nenint
möchte, dann schreib ichstatic_cast<int>(value + 0.5)
(bzw.0.5f
wennvalue
einfloat
ist).
(numeric_cast
hätte ich ja noch irgendwo verstanden, obwohl auch das halbwegs plem wäre.)
-
daki schrieb:
arg.. ok.. hab da zuviel reininterpretiert.. hab aufgrund der char ... gemutmaßt, dass da noch irgendwie mit strings rumhantiert wird.
Ne, das sind Werte die aus nem Register eines IO Chips kommen. Ich hätte hier einfach unsigned int genommen, oder ggf. BYTE oder uint8_t. unsigned char ist aber das kleinste WTF in dem Code.
Nebenbei bemerkt: in a und b stehen unterschiedliche Bits eines Werts der auf zwei Register aufgeteilt ist. Das selbe Bit kann also nie in a UND b gesetzt sein. Ich hätte mir hier also ein a | b erwartet. Das hätte mir zumindest ca. 5 Minuten grübeln erspart, darüber was hier eigentlich überhaupt abgeht.
aber was kann einem dazu verführen hier nen lexical cast zu machen?
Ich weiss es nicht.
Vermutlich der neu gefundene Hammer der bekanntlich alle Probleme zu Nägeln macht.
Oder die pure Freude daran bekloppten Code zu schreiben.@Ethon
[haarspalt]
lexical_cast schickt genaugenommen nie irgendwas in einen stringstream, sondern verwendet eine super_special_lexical_cast_stream.
[/haarspalt]
Der super_special_lexical_cast_stream ist zwar deutlich schneller als ein stringstream, aber so wirklich gut wird die Sache dadurch auch nicht.
-
Der lexical_cast für float ist echt das Sahnehäubchen. Da wird der Compiler noch mühevoll noch den float in ein double umwandeln müssen. Daher empfiehlt sich immer
-Wdouble-promotion
mit dem GCC. Vorallem hat der GCC zB ein builtin-pow mit Integern und hätte das dann vermutlich zumindest in ein 2<< umwandeln können. Die stringstreams in lexical_cast sind aber tödlich. Die sind ja vermutlich zu tief geschachtelt um geinlined werden zu können. Die boost-Leute arbeiten wohl an einer Alternativen zu lexical_cast, die eben nicht immer die stringstreams nutzt.
-
Also, der gcc 4.61 mit -03:
#define NO_INLINE __attribute__ ((noinline)) NO_INLINE int foo() { unsigned char a = 1; unsigned char b = 2; return boost::lexical_cast<int>(std::pow(2.0, static_cast<float>(a + b))); }
Dump of assembler code for function _Z3foov:
0x0000000000401ca0 <+0>: sub $0x38,%rsp 0x0000000000401ca4 <+4>: mov %rsp,%rdi 0x0000000000401ca7 <+7>: mov %fs:0x28,%rax 0x0000000000401cb0 <+16>: mov %rax,0x28(%rsp) 0x0000000000401cb5 <+21>: xor %eax,%eax 0x0000000000401cb7 <+23>: callq 0x401900 <_ZN5boost6detail12lexical_castIidLb0EcEET_NS_11call_traitsIT0_E10param_typeEPT2_m.constprop.17> 0x0000000000401cbc <+28>: mov 0x28(%rsp),%rdx 0x0000000000401cc1 <+33>: xor %fs:0x28,%rdx 0x0000000000401cca <+42>: jne 0x401cd1 <_Z3foov+49> ---Type <return> to continue, or q <return> to quit--- 0x0000000000401ccc <+44>: add $0x38,%rsp 0x0000000000401cd0 <+48>: retq 0x0000000000401cd1 <+49>: callq 0x4016b0 <__stack_chk_fail@plt>
Sobald a und b nicht mehr Compilezeitkonstanten sind fliegt pow nicht mehr raus, dann ist es 100% fail.
Die boost-Leute arbeiten wohl an einer Alternativen zu lexical_cast, die eben nicht immer die stringstreams nutzt.
Wieso nicht einfach alle gängigen Konvertierungen überladen und unter der Haube zb die C-API nutzen?
-
Ethon schrieb:
Wieso nicht einfach alle gängigen Konvertierungen überladen und unter der Haube zb die C-API nutzen?
Das wäre zu einfach. Es gibt ein ungeschriebenes Gesetz, das besagt, dass Boost-Code overengineered und unlesbar sein muss. (Und leider ist mein Scherz nur all zu wenig Scherz und viel zu nahe an der Wahrheit :()
-
Nein, laut der Boost Webseite ist boost::lexical_cast in vielen Fällen (fast allen) schneller als die C-API
-
pyhax schrieb:
Nein, laut der Boost Webseite ist boost::lexical_cast in vielen Fällen (fast allen) schneller als die C-API
Das? http://www.boost.org/doc/libs/1_48_0/doc/html/boost_lexical_cast/performance.html
Warum soll man bitte sprintf verwenden, wenns um Performance geht? Einfach nur lächerlich der Vergleich.
-
Tippgeber schrieb:
Ethon schrieb:
Wieso nicht einfach alle gängigen Konvertierungen überladen und unter der Haube zb die C-API nutzen?
Das wäre zu einfach. Es gibt ein ungeschriebenes Gesetz, das besagt, dass Boost-Code overengineered und unlesbar sein muss. (Und leider ist mein Scherz nur all zu wenig Scherz und viel zu nahe an der Wahrheit :()
Joah, leider oft zu wahr. Wenn das Overengineering wenigstens so wäre, dass man eigene Konvertierungsfunktionen registrieren könnte ...
Nein, laut der Boost Webseite ist boost::lexical_cast in vielen Fällen (fast allen) schneller als die C-API
Nö.
String-to-Int: http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/
Und so weiter: http://tinodidriksen.com/category/code/cpp/
Lexical Cast stinkt immer ab.