Ich weiss es nicht wirklich, eine Spekulation, die im Rahmen von effizientem Code ggf. greift: wenn man moeglichst schnell moeglichst viel rechnen will, benutzt man ueblicherweise die entsprechenden Prozessor-Erweiterungen wie SSE2 & Co. Und ein entscheidender Anteil an schnellem Code hat auch das Vermeiden von Verzweigungen. Darum kann es sich oft lohnen, einen Algorithmus etwes "verquer" durch mathematische Operationen auszudruecken. Ein kleines, harmloses Beispiel: ich brauche gerade Ringbuffer fuer uC-Programmierung in Assembler. Der hat einen schreibe und einen lese-Zeiger bzw. Index. Der Fuellgrad ergibt sich dadurch aus der Differenz dieser beiden Werte:
Aber halt - das ist ja ein Ringbuffer! Es kann also vorkommen, dass write_pos < read_pos ist, und ich eine negative Zahl erhalte. So ist es also richtig:
Code: Alles auswählen
filled = write_pos - read_pos
if filled < 0:
filled += buffer_size
Und schon habe ich mir eine Verzweigung eingehandelt.
Es geht aber auch so:
Code: Alles auswählen
filled = (write_pos + buffer_size - read_pos) % buffer_size
Ich erzwinge also, dass write_pos immer relativ groesser ist als read_pos, aber ggf. bin ich "zu gross" und falte das Ergebnis dann wieder in die Buffergroesse. Und der Vorteil: alles Maschineninstruktionen (zB % einfach AND), die ohne Verzweigung laufen, und die Pipeline voll halten.
Und da kann ich mir vorstellen, dass es eben Algorithmen gibt, die von copysign in dieser Art profitieren.
Wirklich belastbares habe ich dazu aber auch nicht gefunden. In der C++-std-lib steht immerin das hier:
https://en.cppreference.com/w/cpp/numeric/math/copysign
std::copysign is the only portable way to manipulate the sign of a NaN value (to examine the sign of a NaN, signbit may also be used)
Das hat wahrscheinlich was mit IEE754 zu tun.