Почему c # не может вычислить точные значения математических функций - PullRequest
9 голосов
/ 05 января 2011

Почему c # не может выполнять точные операции.

Math.Pow(Math.Sqrt(2.0),2) == 2.0000000000000004

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

Редактировать

Речь идет не о моем калькуляторе, я просто привел пример:

http://www.wolframalpha.com/input/?i=Sqrt%282.000000000000000000000000000000000000000000000000000000000000000000000000000000001%29%5E2

Приветствия

Ответы [ 8 ]

8 голосов
/ 05 января 2011

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

Другой вариант заключается в том, что калькулятор запоминает операции, которые привели к предыдущемурезультаты и применение алгебры для отмены операций ... хотя это кажется маловероятным..NET конечно не будет пытаться это сделать - он вычислит промежуточное значение (корень из двух), а затем возведет его в квадрат.

Если вы думаете, вы может быть лучше, я предлагаю вам выписать квадратный корень из двух до (скажем) 50 десятичных знаков, а затем точно возвести в квадрат.Посмотри, получишь ли ты ровно 2 ...

2 голосов
/ 05 января 2011

Ваш калькулятор не вычисляет его точно, просто ошибка округления настолько мала, что не отображается.

1 голос
/ 05 января 2011

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

0 голосов
/ 05 января 2011

Это было упомянуто ранее, но я думаю, что вы ищете компьютерная алгебраическая система. Примерами этого являются Maxima и Mathematica, и они предназначены исключительно для предоставления точных значений для математических вычислений, чего-то, что не покрывается ЦП.

Математические процедуры в таких языках, как C #, предназначены для численных расчетов: ожидается, что если вы выполняете вычисления как программу, вы уже упростили бы ее или вам понадобится только числовой результат.

0 голосов
/ 05 января 2011

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

Например: 2 ^ (1/2), скорее всего, не вычисляется как число в калькуляторе, если вы явно не указали это сделать (как в ti89 / 92).

Кроме того, калькулятор имеет логику, которую он может использовать для управления ими, например, x ^ (1/2) * y ^ (1/2) = (x * y) ^ 1/2, где он может затем стирать, ополаскивать, повторите метод для работы с иррациональными значениями.

Если бы вы дали c # какой-то способ сделать это, я полагаю, что это тоже возможно. В конце концов, алгебраические решатели, такие как математика, не являются магическими.

0 голосов
/ 05 января 2011

2.0000000000000004 и 2. оба представлены как 10. с одинарной точностью.В вашем случае использование одинарной точности для C # должно дать точный ответ

В другом примере Wolfram Alpha может использовать для расчетов более высокую точность, чем точность станка.Это добавляет большую потерю производительности.Например, в Mathematica при повышении точности вычисления в 300 раз медленнее

k = 1000000;
vec1 = RandomReal[1, k];
vec2 = SetPrecision[vec1, 20];
AbsoluteTiming[vec1^2;]
AbsoluteTiming[vec2^2;]

Это 0,01 секунды против 3 секунд на моем компьютере

Вы можете увидеть разницу в результатах, используя одинарную точностьДвойная точность введена в Java следующим образом:

public class Bits {
    public static void main(String[] args) {
    double a1=2.0;
    float a2=(float)2.0;
    double b1=Math.pow(Math.sqrt(a1),2);
    float b2=(float)Math.pow(Math.sqrt(a2),2);
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(a1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(a2)));
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(b1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(b2)));
    }
}

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

0 голосов
/ 05 января 2011

Способ округления чисел в калькуляторах зависит от модели. Мой TI Voyage 200 делает алгебру, чтобы упростить уравнения (среди прочего), но большинство калькуляторов будет отображать только часть реального значения, вычисленного после применения функции округления к результату. Например, вы можете найти квадратный корень из 2, и калькулятор будет хранить (скажем) 54 знака после запятой, но будет отображать только 12 округленных знаков после запятой. Таким образом, если сделать квадратный корень из 2, то степень этого результата, равная 2, вернет то же значение, так как результат округляется. В любом случае, если калькулятор не может хранить бесконечное число десятичных знаков, вы всегда получите наилучший приблизительный результат от сложных операций.

Кстати, попробуйте представить 10.0 в двоичном виде, и вы поймете, что вы не можете представить его равномерно, и в итоге вы получите (что-то вроде) 10.00000000000..01

0 голосов
/ 05 января 2011

С чего вы взяли, что ваш калькулятор может это сделать? Он почти наверняка отображает меньше цифр, чем рассчитывает, и вы получите «правильный» результат, если распечатаете свой 2.0000000000000004 только с пятью дробными цифрами (например).

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

Квадратный корень из 2 является одним из тех раздражающих иррациональных чисел, как PI, и поэтому не может быть представлен обычными двойными или даже десятичными типами IEEE754. Чтобы точно представить его, вам нужна система, способная к символической математике, в которой значение сохраняется как «квадратный корень из двух», чтобы последующие вычисления могли дать правильные результаты.

...