Подтвердить что ты не робот

Понимание проблем с плавающей запятой

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

CalculateTotalTax = function (TaxRate, TaxFreePrice) {
     return ((parseFloat(TaxFreePrice) / 100) * parseFloat(TaxRate)).toFixed(4);
};

Мне не удалось ввести какие-либо два значения, которые вызвали для меня неправильный результат для этого метода. Если я удаляю toFixed (4), я могу понять, где вычисления начинают терять точность (где-то около шестого десятичного разряда). Сказав это, мое понимание поплавков заключается в том, что иногда даже небольшие числа могут быть не представлены или я неправильно понял, и их можно было бы точно указать на 4 десятичных знака (например).

MSDN объясняет float как такой...

Это означает, что они не могут представление любого количества, которое а не двоичная дробь (формы k/ (2 ^ n), где k и n - целые числа)

Теперь я предполагаю, что это относится ко всем поплавкам (в том числе к тем, которые используются в javascript).

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

Надеюсь, то, что я прошу, имеет смысл.

4b9b3361

Ответ 1

Начните с чтения Что каждый компьютерный ученый должен знать о плавающей точке: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Короткий ответ: float с двойной точностью (по умолчанию в JavaScript) имеет около 16 десятичных цифр точности. Округление может варьироваться от платформы к платформе. Если абсолютно необходимо, чтобы вы получили правильный ответ, вы должны сделать рациональную арифметику самостоятельно (это не должно быть сложно - для валюты, возможно, вы можете просто умножить на 100, чтобы сохранить число центов как целое число).

Но если достаточно получить ответ с высокой степенью точности, поплавки должны быть достаточно хорошими, особенно двойной точностью.

Ответ 2

Есть две важные вещи, которые вы должны сейчас при работе с float:

1- Вы должны знать машину epsilon. Чтобы узнать, какая у вас точность.

2- Вы не должны считать если два значения равны в базе 10, они равны в базе 2 в машине с предел точности.

if ((6.0 / 10.0) / 3.0 != .2) {
        cout << "gotcha" << endl;
}

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

Ответ 3

Другие ответы указывают на хорошие ресурсы для понимания этой проблемы. Если вы фактически используете денежные значения в своем коде (как в вашем примере), вы должны предпочесть десятичные типы (System.Decimal в .Net). Это позволит избежать некоторых проблем округления при использовании float и лучше соответствовать домену.

Ответ 4

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

Попробуйте .1 * 3, или 162.295/10, или 24.0 + 47.98. Мне это не удается в JS. Но, 24.0 * 47.98 не подводит.

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