Cześć, bawię się w robocie doublami. Jest kawałek kodu gdzie możemy przyjąć pewne założenia co do standardu (IEEE 754) oraz możliwych trzymanych wartości. W tym kawałku kodu muszę w pewnym momencie zaokrąglić wartość i zamienić na inta. Te miejsce kodu jest o tyle ciekawe, że fajnie by było nie używać relatwnie wolnego std::round
. Używam sztuczki wykorzystującej właściwości ieee, żeby w mantysie znalazła się zaokrąglona wartość, potem szybki AND i mamy wszystko w uint64_t
.
Problem jest z zaokrągleniem gdy po przecinku mamy .5
. Oczywiście jak jest piątka, to powinno zaokrąglić w górę. No ale nie zaokrągla. Jak po przecinku są wartości 0.{6,7,8,9}
, to kod śmiga jak trzeba.
Sztuczka jest opisana tu: https://stackoverflow.com/a/17035583
Kod wygląda powiedzmy tak:
std::uint64_t my_round(double d) {
d += 6755399441055744.0;
std::uint64_t roundedValue;
const std::byte *dPtr = reinterpret_cast<const std::byte *>(&d);
std::memcpy(&roundedValue, dPtr, sizeof(roundedValue));
return roundedValue & std::uint64_t{0x7FFFFFFFFFFFF};
}
// Wyteguje się bo `roundedValue == 284464158943456`
assert(my_round(284464158943456.5) == 284464158943457ul)
// Przejdzie
assert(my_round(284464158943456.6) == 284464158943457ul)
No to ten, jakieś pomysły? Założenie jest takie, żeby kod był jak najszybszy. Jak zawlimy wydajność, to mogę równie dobrze wrócić do std::round
:)