Предупреждение о неточных константах с плавающей запятой

Вопросы типа "Почему не 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 + 0,1 = 0,8?" заставил меня думать, что...

... Было бы неплохо, если бы компилятор предупредил о константах с плавающей запятой, что он округляется до ближайшего представимого в двоичном типе с плавающей точкой (например, 0,1 и 0,8 округлены в плавающей точке счисления с плавающей точкой, иначе им понадобится бесконечное количество пространства для хранения бесконечного числа цифр).

Я искал предупреждения gcc и до сих пор не нашел для этой цели (-Wall, -Wextra, -Wfloat-equal, -Wconversion, -Wcoercion (неподдерживаемый или только C), -Wtraditional ( C только), кажется, не делают то, что я хочу).

Я также не нашел такого предупреждения в компиляторе Microsoft Visual С++.

Не хватает ли скрытого или редко используемого параметра?

Существует ли какой-либо компилятор, который имеет такое предупреждение?

EDIT. Это предупреждение может быть полезно для образовательных целей и служить напоминанием тем, кто новый для плавающей точки.

Ответ 1

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

Предупреждение должно быть отключено по умолчанию, чтобы поддерживать основную часть существующего кода с плавающей запятой. Если бы это было доступно, я бы включил его для моего кода в математической библиотеке Mac OS X. Конечно, в библиотеке есть точки, в которых мы зависим от каждого бита значения с плавающей запятой, например, где мы используем арифметику с расширенной точностью, а значения представлены в нескольких объектах с плавающей запятой (например, у нас будет один объект с высокими битами 1/π, другой объект с 1/π минус первый объект и третий объект с 1/π минус первые два объекта, что дает нам около 150 бит 1/π). Некоторые такие значения представлены в шестнадцатеричной плавающей запятой в исходном тексте, чтобы избежать каких-либо проблем с преобразованием десятичных цифр в компилятор, и мы могли бы легко преобразовать любые оставшиеся цифры, чтобы избежать предупреждения нового компилятора.

Однако я сомневаюсь, что мы могли бы убедить разработчиков компилятора, что достаточное количество людей будет использовать это предупреждение или что оно будет ловить достаточное количество ошибок, чтобы оправдать их время. Рассмотрим случай libm. Предположим, что мы вообще писали точные цифры для всех констант, но, в одном случае, написали другую цифру. Будет ли это предупреждение ловить ошибку? Ну, что ж, ошибка? Скорее всего, цифра преобразуется точно в значение, которое мы хотели в любом случае. При написании кода с включенным предупреждением мы, скорее всего, думаем о том, как будут выполняться вычисления с плавающей запятой, и значение, которое мы написали, является подходящим для нашей цели. Например, это может быть коэффициент для некоторого минимаксного многочлена, который мы вычислили, и коэффициент так же хорош, как он собирается получить, независимо от того, представлен ли он приблизительно в десятичной форме или преобразован в некоторую точно представляемую шестнадцатеричную цифру с плавающей запятой.

Итак, это предупреждение редко ломает ошибки. Возможно, он поймал бы случай, когда мы заблуждались цифрой, случайно вставляя лишнюю цифру в шестнадцатеричную цифру с плавающей запятой, заставляя ее выходить за пределы представляемого значения. Но это редко. В большинстве случаев используемые нами цифры являются либо простыми, либо короткими или копируются и вставляются из программного обеспечения, которое их рассчитало. В некоторых случаях мы будем вводить специальные значения, такие как 0x1.fffffffffffffp0. Предупреждение, когда лишний "f" проскальзывает в эту цифру, может вызвать ошибку во время компиляции, но эта ошибка почти наверняка будет быстро схвачена при тестировании, поскольку она резко изменяет специальное значение.

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

Ответ 2

Не будет такого компилятора, и причина очевидна. Мы записываем двоичные компоненты в десятичной форме:

Первый дробный бит равен 0,5

Второй дробный бит равен 0,25

Третий дробный бит равен 0,125

....

Вы видите это? Из-за нечетных окончаний с номером 5 каждый бит требует другое десятичное число, чтобы точно представлять его. Один бит нужен один десятичный, два бита требуется два десятичных знака и т.д.

Итак, для дробных плавающих точек это будет означать, что для большинства десятичных чисел вам нужно 24 (!) десятичных разряда для одноточечных поплавков и 53 (!!) десятичные цифры для двойной точности. Хуже того, точные цифры не содержат дополнительной информации, они являются чистыми артефактами вызванное изменением базы.

Никто не собирается записывать 3.141592653589793115997963468544185161590576171875 для pi, чтобы избежать предупреждения компилятора.

Ответ 3

Предупреждение находится в источнике: когда вы пишете float, double или long double, включая любые их соответствующие литералы. Очевидно, что некоторые литералы точны, но даже это не очень помогает: сумма двух точных значений может быть неточной, например, если они имеют довольно разные масштабы. Предоставление компилятору предупреждения о неточных константах с плавающей запятой порождает ложное чувство безопасности. Кроме того, что вы собираетесь делать с округленными константами? Написание точного ближайшего значения явно было бы подвержено ошибкам и запутывало намерение. Написание их по-разному, например, запись 1.0 / 10.0 вместо 0.1 также затуманивает намерение и может давать разные значения.

Ответ 4

Я не вижу, как компилятор узнает или что компилятор может предупредить вас о чем-то подобном. Просто совпадение состоит в том, что число может быть точно представлено чем-то, что по своей сути является неточным.