С# - числовые типы переменных - нотации Base 2/Base 10

есть одна маленькая вещь, делающая меня головоломкой в С# :)

Вот мои переменные и результаты:

decimal a1 = 0.2M;
decimal a2 = 1.0M;

a1 - a2 = -0.8

float b1 = 0.2F;
float b2 = 1.0F;

b1 - b2 = -0.8

****** c1 = 0.2;
****** c2 = 1.0;

c1 - c2 = -0.8

****** x1 = 0.2F;
****** x2 = 1.0F;

x1 - x2 = -0.799999997019768

Decimal - результат для меня так же, как и ожидалось, зная, что они работают в базовых 10 нотациях.

Float - Удивил меня, зная, что он работает с нотной базой 2, он на самом деле показывает результат, как если бы он работал в качестве нотации base 10 без потери точности.

****** c - То же, что и для Float.

Двойной x - показывает результат, который я ожидал бы для Float.

Вопрос в том, что происходит с группами Float, ****** 'c' и 'x'? Почему ****** 'x' группа потеряла свою точность, тогда как группа Float фактически рассчитана в нотации base 10, давая так сказать "ожидаемый результат" из расчета? Интересно, почему объявление числа типов ****** x group как F так резко изменило его?

для чего это стоило, я бы только ожидал, что группа Decimal даст мне результат -0.8, а все остальные - "-0.799999997019768".

похоже, что мне не хватает какой-то связи понимания, которая имеет место в том, как учитывается расчет.

2 ответа

Во-первых - это не имеет ничего общего с базой 2 и базой 10. Все ваши значения представлены с использованием базы 10. База 2 не используется вообще в этом коде.

похоже, что мне не хватает какой-то связи понимания, которая имеет место в том, как учитывается расчет.

Оба float и ****** используют представление с плавающей запятой для чисел, что не является на 100% точным. Когда вы показываете значение, происходит округление. Поскольку float имеет по своей сути меньшую точность, он округляется до меньшего числа знаков после запятой, что может сделать его "более точным" в некоторых случаях (хотя и менее точным).

В вашем случае причина, по которой группа "x" и группа "c" отличаются друг от друга, заключается в том, что вы объявляете переменные x1 и x2 следующим образом:

****** x1 = 0.2F;
****** x2 = 1.0F;

Это, фактически, то же самое, что и делать:

float temp = 0.2f;
****** x1 = temp; // Convert from float to ******, which has a loss of precision
temp = 1f;
****** x2 = temp; // Convert from float to ******, which has a loss of precision

Таким образом, x1 и x2 не имеют точно таких же значений, как c1 и c2. Это приводит к еще большей потере точности, что позже, когда происходит вычитание, достаточно, чтобы печать больше не крутилась.

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


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

В группе x ****** переменные уже имеют дополнительную точность, но включение постоянной константы float -type в них может привести к тому, что компилятор будет вести себя по-разному, что приведет к получению менее точных результатов. Поскольку парные пары уже немного от того, где они должны быть, полная ****** точность просто переносит ошибку вперед.

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

О, и ничто из этого не имеет ничего общего с базой 2.

licensed under cc by-sa 3.0 with attribution.