Clang-tidy неправильное округление

Документация для clang-tidy [bugprone-incorrect-roundings] проверка говорит:

Число 0,499999975 (наименьшее представимое число с плавающей точкой ниже 0,5) округляется до 1,0.

Насколько я могу определить, наименьшее число с плавающей запятой ниже 0.5 равно 0.4999999702, а не 0.499999975. Но, несмотря на это, оба числа дают мне 0 значения при наивном округлении:

#include <iostream>

int main() {

    const float v1 = 0.499999975;
    const float v2 = 0.4999999702;

    std::cout << (int)(v1+0.5) << "\n" 
              << (int)(v2+0.5) << "\n";
}

Я что-то упускаю?


person αλεχολυτ    schedule 27.03.2019    source источник


Ответы (1)


По поводу арифметического преобразования в стандарте:

6.3.1.8 Обычные арифметические преобразования

...

  • В противном случае, если соответствующий действительный тип любого из операндов является двойным, другой операнд преобразуется без изменения домена типа в тип, соответствующий вещественный тип которого является двойным.

  • Значения операндов с плавающей запятой и результатов выражений с плавающей запятой могут быть представлены с большей точностью и диапазоном, чем это требуется типом;

Итак, в этой строке:

(int)(v1+0.5) 

Ваша переменная v1 продвигается к операции с плавающей запятой двойной точности, поэтому вы получаете ноль.

Это должно решить вашу проблему:

#include <iostream>

int main() {

    const float v1 = 0.499999975f;
    const float v2 = 0.4999999702f;

    std::cout << (int)(v1 + 0.5f) << "\n" 
              << (int)(v2 + 0.5f) << "\n";
}
person mohabouje    schedule 27.03.2019
comment
Но никто не набирает +0.5f даже в аккуратном примере. Следовательно, +0.5 должно работать хорошо, не так ли? - person αλεχολυτ; 27.03.2019
comment
@αλεχολυτ не совсем так. Double имеет достаточно битов для представления v1 + 0,5 0,99999975, которое усекается до нуля (int). Но у числа с плавающей запятой слишком мало бит, поэтому оно округляется до 1,0, а позже (int) усекается до 1. - person Michael Veksler; 27.03.2019
comment
@αλεχολυτ Также обратите внимание, что это зависит от режима округления. std::fesetround(FE_DOWNWARD); всегда будет возвращать 0, даже для числа с плавающей запятой - person Michael Veksler; 27.03.2019
comment
Код @MichaelVeksler из документации говорит double_expression, и, поскольку 0.5 тоже двойное, я все еще не вижу проблемы с таким округлением (для положительных значений). - person αλεχολυτ; 27.03.2019
comment
В операции с двойной точностью число 0,499999975 не округляется до 0,5, поэтому при добавлении 0,5 получается ~0,999999975. Если добавить 0,5f, операция выполняется с одинарной точностью, поэтому число 0,499999975f округляется до 0,5f, а результат равен 1,0f. - person mohabouje; 27.03.2019
comment
@αλεχολυτ В этом случае проблема связана с чистыми значениями с плавающей запятой. С double значения немного другие: 0,499999999999999944, но эффект тот же. (int)(0.499999999999999944 + 0.5) округляется до 1 в зависимости от режима округления. Попробуйте - person Michael Veksler; 27.03.2019
comment
@MichaelVeksler Кажется, что в документации следует избавиться от типов с плавающей запятой и оставить только двойной, чтобы устранить путаницу. - person αλεχολυτ; 27.03.2019