Как лучше всего округлить большие числа с плавающей запятой без знака до целых чисел в C ++? - PullRequest
5 голосов
/ 13 июля 2020

Мне нужно округлить 64-битное двойное значение до ближайшего uint64_t в моем коде со стандартным поведением округления средней точки "math.h". У некоторых из этих чисел есть свойство:

  • Больше, чем LLONG_MAX
  • Меньше или равно ULLONG_MAX

Я заметил, что нет версии без знака стандартных функций округления библиотеки. Кроме того, я подозреваю, что некоторые из уловок, которые люди используют для этого, больше не работают, когда промежуточное двойное значение слишком велико, чтобы поместиться в мантиссу.

Как лучше всего выполнить это преобразование?

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

Я бы использовал std::round, а затем ограничил диапазон uint64. Также вам нужно решить, что делать с NaN.

std::round не преобразуется в целое число. Обратите внимание, что встроенное преобразование с плавающей запятой в целое число в C и C ++ усекает и не определено для значений вне диапазона.

std::round реализует стандартный "школьный" тип округления до ближайшего целого числа со средней точкой. случаев округления в большую сторону (например, round(1.5) == 2; round(-1.5) = -2).

Текущий режим округления игнорируется, также это не округление IEEE754 до ближайшего. Последний будет использовать round half to even и округлять t ie случаи разбиения вверх и вниз, чтобы уменьшить статистическую погрешность.

Для long long можно было бы сделать что-то вроде этого.

double iv = std::round(val);
if (std::isnan(iv)) {
    return 0LL;
} else if (iv > LLONG_MAX)
   return LLONG_MAX;    
} else if (iv < LLONG_MIN) {
   return LLONG_MIN;
} else {
   return (long long)iv;
}

Соответственно делается и корпус unsigned long long.

double iv = std::round(val);
if (std::isnan(iv)) {
    return 0ULL;
} else if (iv > ULLONG_MAX)   // ok, if you know your values are less this can be spared of course
   return ULLONG_MAX;    
} else if (iv < 0) {
   return 0ULL;
} else {
   return (unsigned long long)iv;
}
1 голос
/ 13 июля 2020

Самый простой способ округления - приведение к типу назначения: Представьте, что вы хотите округлить 2,3 до целого числа root, затем вы делаете:

return (int) 2.3;

Итак, в общем, вы делаете:

return (destination_type) x;

Однако здесь у вас есть проблема что вы всегда округляете в меньшую сторону, как вы можете видеть в этом примере 2.7:

return (int) 2.7;
=> yields 2

Если вы хотите округлить до ближайшего, вам нужно добавить 0,5, а затем округлить:

return (int) (2.7 + 0.5);
=> yields 3

Итак, если вы хотите округлить до любого типа назначения, используя ближайшее округление, вам нужно:

return (destination_type) (x + 0.5);
...