«Украсить» число, округлив ошибочные цифры надлежащим образом.

Я хочу свой торт и съесть его. Я хочу украсить (округлить) числа в максимально возможной степени, не ставя под угрозу точность других вычислений. Я использую двойники в С# (также с некоторыми манипуляциями с преобразованием строк).

Вот в чем проблема. Я понимаю ограничения, присущие представлению чисел double (поэтому, пожалуйста, не объясняйте это). ОДНАКО, я хочу каким-то образом округлить число, чтобы оно выглядело эстетически приятным для конечного пользователя (я делаю калькулятор). Проблема в том, что округление по X значащих цифр работает в одном случае, но не в другом, а округление до десятичного знака работает в другом, но не в первом случае.

Наблюдать:

  • СЛУЧАЙ A: Math.Sin(Math.Pi) = 0,000000000000000122460635382238
  • СЛУЧАЙ B: 0,000000000000001/3 = 0,0000000000000003333333333333333

В первом случае я хочу округлить до ДЕСЯТИЧНЫХ ЗНАКОВ. Это дало бы мне хороший аккуратный ноль, который я ищу. Округление по цифрам Sig означало бы, что я тоже оставлю ошибочные цифры.

Однако во втором случае я хочу округлить ЗНАЧИТЕЛЬНЫЕ ЦИФРЫ, так как я потеряю массу точности, если округлю только десятичные знаки.

Есть ли общий способ, которым я могу удовлетворить оба типа расчета?


person Dan W    schedule 18.07.2013    source источник
comment
Я предлагаю вам изучить научную нотацию. Любой пользователь, который будет использовать этот вид математики, знает это, и он предназначен для этих смехотворно больших и маленьких чисел.   -  person Logarr    schedule 18.07.2013
comment
Я знаю о научной нотации. Я использовал полное цифровое представление, подобное этому, чтобы, возможно, было проще смотреть и думать о проблеме.   -  person Dan W    schedule 18.07.2013
comment
Это дало бы мне хороший аккуратный ноль, который я ищу. - Это не думать о проблеме в рамках научной нотации. Проблема форматирования довольно проста; вам нужно преобразовать в SN, а затем сделать стандартное округление на основе десятичных знаков. По сути, я хочу сказать, что не пытайтесь заново изобретать калькулятор.   -  person Logarr    schedule 18.07.2013
comment
Отображение этих цифр как SN не изменит сути вопроса. Округление по десятичным разрядам также не будет работать для СЛУЧАЯ B, где все конечные 3 действительны и не являются ошибочными, в отличие от последних цифр в случае A. Пожалуйста, см. ответ Стена ниже для получения дополнительной информации.   -  person Dan W    schedule 18.07.2013


Ответы (3)


Я не думаю, что это возможно сделать с самим результатом, и точность не имеет к этому никакого отношения.

Рассмотрим этот ввод: (1+3)/2^3 . Вы можете «украсить» его, показывая результат как sin(30) или cos(60) или 1/2 и множество других интерпретаций. Выбор неправильного «украшения» может ввести пользователей в заблуждение, заставив их думать, что их функция как-то связана с sin(x).

Если ваш калькулятор сохраняет все исходные данные в виде переменных, вы можете отложить все операции до тех пор, пока вам не понадобится результат, а затем убедитесь, что вы упростили результат, пока он не будет соответствовать вашим потребностям. И вам нужно подумать об использовании рациональных чисел, с e, Pi и другими иррациональными числами может быть не так просто иметь дело.

person Sten Petrov    schedule 18.07.2013
comment
Я предполагаю, что мне нужно проверить, есть ли в калькуляторах Pi или e и т. Д., А затем действовать соответственно. Для более сложных расчетов с показателями степени, трансцендентными числами, крошечными числами с большим количеством нулей и триггерами, летящими повсюду, я вижу, что это становится очень, очень грязным :( - person Dan W; 18.07.2013
comment
Я бы не сказал, что это грязно, я бы сказал, что почти невозможно сделать это правильно, и к тому времени, когда вы закончите, вам придется продавать свое программное обеспечение по 1000 долларов за коробку :) - person Sten Petrov; 18.07.2013
comment
Ого, не думала, что ТАК сложно. Интересно, может ли что-нибудь там справиться с такими вещами. - person Dan W; 18.07.2013
comment
Попробуйте Mathlab или WolframAlpha.com - person Sten Petrov; 18.07.2013
comment
Если вы смотрите на общенаучный калькулятор - да, я бы сказал, что это так сложно. Сделайте это простым офисным калькулятором и придерживайтесь рациональных чисел — это осуществимо, все, что вам нужно, — это процедура упрощения дробей, синтаксический анализатор RPN и некоторые эвристики, определяющие, когда прекратить выполнение операций и когда переключать альтернативные операнды. - person Sten Petrov; 18.07.2013

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

По умолчанию используйте разумное количество значащих цифр для нескольких вычислений в формате с плавающей запятой, который вы используете внутри — около 12, если вы используете двойное число. Если пользователь изменит формат, немедленно отобразится в новом формате.

person Patricia Shanahan    schedule 18.07.2013
comment
Я хочу, чтобы он автоматически определялся в соответствии с контекстом. Например, если пользователь выбирает 8 знаков, то для случая B результатом будет 0,000000000000000333333333, что хорошо! Однако в случае A будет 0,00000000000000012246064, что плохо, поскольку вместо этого нам нужен нулевой результат. Я не хочу, чтобы пользователь постоянно переключался между использованием SD и DP в зависимости от типа суммы. - person Dan W; 18.07.2013
comment
Вы всегда можете позволить пользователю указать наименьшую величину, которую следует рассматривать как ненулевую. - person Patricia Shanahan; 19.07.2013

Лучшее решение — использовать произвольная-точность и/или символическая арифметика, хотя это приводит к гораздо более сложному коду и более медленной скорости. Но поскольку производительность для калькулятора не важна (в случае калькулятора с кнопками, а не того, для расчета которого вы вводите выражения), вы можете использовать их без проблем.

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

Изменить: только что нашел хорошее решение, но не имел возможности попробовать.

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

До публикации Стила и Уайта Как правильно печатать числа с плавающей запятой, реализации printf и подобные функции рендеринга делали все возможное для рендеринга чисел с плавающей запятой, но их поведение сильно различалось. Например, такое число, как 1,3, может быть воспроизведено как 1,29999999, или если число было помещено в цикл обратной связи, заключающийся в записи и считывании его письменного представления, каждый последующий результат может отдаляться все дальше и дальше от оригинала.

...

В 2010 году Флориан Лойч опубликовал замечательную статью на языке PLDI: вычисляет быстро и точно с помощью целых чисел, что представляет собой самый большой шаг в этой области за 20 лет: он в основном понял, как использовать машинные целые числа для точного рендеринга! Почему я говорю в основном? Потому что, хотя алгоритм Лойча Grisu3 очень быстр, он отказывается примерно от 0,5% чисел, и в этом случае вам придется вернуться к Dragon4 или его производным.

А вот и драконы: успехи в решении проблем, о которых вы даже не подозревали

person phuclv    schedule 27.07.2013