Округление? Почему так?
От: zbanned  
Дата: 03.07.08 05:36
Оценка: 4 (2)
Почему так? Округление?

C#:
int f;
f = (int)(10 * 0.9f); //returns 8
f = Convert.ToInt16(10 * 0.9f); //returns 9
Re: Округление? Почему так?
От: Александр Кузнецов Россия  
Дата: 03.07.08 05:56
Оценка: 4 (1)
Здравствуйте, zbanned, Вы писали:

Z>Почему так? Округление?


Z>C#:

Z>
Z>int f;
Z>f = (int)(10 * 0.9f); //returns 8
Z>f = Convert.ToInt16(10 * 0.9f); //returns 9
Z>


Да. Округление. (см. MSDN)
public static short ToInt16 (
    double value
)

Return Value
value rounded to the nearest 16-bit signed integer. If value is halfway between two whole numbers, the even number is returned; that is, 4.5 is converted to 4, and 5.5 is converted to 6.

В то время как явное приведение типов через (int) возвращает ближайшее целое, меньшее, чем результат. А что касается внутреннего представления float, то никто не гарантирует, что 0.9f в памяти не хранится как 0.899999. Отсюда и проблемы с преобразованием.
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Re[2]: Округление? Почему так?
От: Аноним  
Дата: 03.07.08 06:11
Оценка:
Где логика?)

int f1 = (int)(10* 0.9f); // 8
int f2 = (int)(10* 0.8f); // 8
int f3 = (int)(10* 0.7f); // 6
int f4 = (int)(10* 0.6f); // 6
int f5 = (int)(10* 0.5f); // 5
int f6 = (int)(10* 0.4f); // 4
int f7 = (int)(10* 0.3f); // 3
int f8 = (int)(10* 0.2f); // 2
Re[3]: Округление? Почему так?
От: _d_m_  
Дата: 03.07.08 06:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Где логика?)


Дьявол в деталях.

здесь
Автор: _d_m_
Дата: 19.05.08
Re[3]: Округление? Почему так?
От: Александр Кузнецов Россия  
Дата: 03.07.08 06:36
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>Где логика?)


Вот здесь:

double f1 = (10 * 0.9f); // 8
double f2 = (10 * 0.8f); // 8
double f3 = (10 * 0.7f); // 6
double f4 = (10 * 0.6f); // 6
double f5 = (10 * 0.5f); // 5
double f6 = (10 * 0.4f); // 4
double f7 = (10 * 0.3f); // 3
double f8 = (10 * 0.2f); // 2

Console.WriteLine("{0}-{1}", (int)f1, f1);
Console.WriteLine("{0}-{1}", (int)f2, f2);
Console.WriteLine("{0}-{1}", (int)f3, f3);
Console.WriteLine("{0}-{1}", (int)f4, f4);
Console.WriteLine("{0}-{1}", (int)f5, f5);
Console.WriteLine("{0}-{1}", (int)f6, f6);
Console.WriteLine("{0}-{1}", (int)f7, f7);
Console.WriteLine("{0}-{1}", (int)f8, f8);


Результаты:
8-8.99999976158142
8-8.00000011920929
6-6.99999988079071
6-6.00000023841858
5-5
4-4.00000005960464
3-3.00000011920929
2-2.00000002980232
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Re[4]: Округление? Почему так?
От: zbanned  
Дата: 03.07.08 07:57
Оценка:
АК>Результаты:
АК>
АК>8-8.99999976158142
АК>8-8.00000011920929
АК>6-6.99999988079071
АК>6-6.00000023841858
АК>5-5
АК>4-4.00000005960464
АК>3-3.00000011920929
АК>2-2.00000002980232
АК>


Если заглянуть вглубь, то как представлена константа в формате IEEE c плавающей точкой?
Когда мы пишем 0.9 = то мантисса = 899999976158142 и запятая помещается после первого разряда?
Тогда почему бы мантиссу не представить как 9? Ведь все можно проще сделать? Откуда погрешность возникает?
Re[5]: Округление? Почему так?
От: _d_m_  
Дата: 03.07.08 08:04
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Тогда почему бы мантиссу не представить как 9? Ведь все можно проще сделать? Откуда погрешность возникает?


Потому что экспонента — степень двойки, а не 10-ки.
Re[6]: Округление? Почему так?
От: Овощ http://www.google.com
Дата: 03.07.08 10:15
Оценка:
Z>>Тогда почему бы мантиссу не представить как 9? Ведь все можно проще сделать? Откуда погрешность возникает?

Мантисса не может быть "представлена как 9", т.к. она хранится в нормализованном виде, т.е. для чисел с плавающей запятой (в десятичном представлении): 0.5 <= Мантисса < 1.0. И вот из-за неточного представления десятичных дробей в двоичном коде и появляется погрешность.
Re[7]: Округление? Почему так?
От: zbanned  
Дата: 03.07.08 10:57
Оценка:
Здравствуйте, Овощ, Вы писали:

Z>>>Тогда почему бы мантиссу не представить как 9? Ведь все можно проще сделать? Откуда погрешность возникает?


О>Мантисса не может быть "представлена как 9", т.к. она хранится в нормализованном виде, т.е. для чисел с плавающей запятой (в десятичном представлении): 0.5 <= Мантисса < 1.0. И вот из-за неточного представления десятичных дробей в двоичном коде и появляется погрешность.


Все равно не понял.
0.9 представим как a*(q^n)
а = мантисса = 9
q = система счисления = множитель = 10
n = показатель степени = -1
Итого: 0,9=9*10^-1
Откуда погрешность?
Если у нас число с плавающей запятой, то
— мантиссу 9 представим в двоичном виде как 1001
— множитель 10 = 1010
— степень -1 = 1111...11 (кол-во 1-ц в зависимости от разрядности)
Все это можно упаковать в 32 битное число с плавающей запятой без погрешности.

Поясните, где не прав?
Re[8]: Округление? Почему так?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 03.07.08 11:07
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Здравствуйте, Овощ, Вы писали:


Z>>>>Тогда почему бы мантиссу не представить как 9? Ведь все можно проще сделать? Откуда погрешность возникает?


О>>Мантисса не может быть "представлена как 9", т.к. она хранится в нормализованном виде, т.е. для чисел с плавающей запятой (в десятичном представлении): 0.5 <= Мантисса < 1.0. И вот из-за неточного представления десятичных дробей в двоичном коде и появляется погрешность.


Z>Все равно не понял.

Z>0.9 представим как a*(q^n)
Z>а = мантисса = 9
Z>q = система счисления = множитель = 10
Z>n = показатель степени = -1
Z>Итого: 0,9=9*10^-1
Z>Откуда погрешность?
Z>Если у нас число с плавающей запятой, то
Z>- мантиссу 9 представим в двоичном виде как 1001
Z>- множитель 10 = 1010
Z>- степень -1 = 1111...11 (кол-во 1-ц в зависимости от разрядности)
Z>Все это можно упаковать в 32 битное число с плавающей запятой без погрешности.

Z>Поясните, где не прав?


А ты распиши также число 9,346242 и поймешь в чем неправ.

На самом деле в компьютере q = 2
Re[9]: Округление? Почему так?
От: zbanned  
Дата: 03.07.08 11:27
Оценка:
G>А ты распиши также число 9,346242 и поймешь в чем неправ.
G>На самом деле в компьютере q = 2

Ок. Давай пока по проще. Хорошо, возмем q=2. Тогда получается большая погрешность:
0,9 ~= 9*2^-3 = 9/8 = 1,125
или
0,9 ~= 9*2^-4 = 9/16 = 0,5625

Откуда тогда берется 0,8999999?
Re[10]: Округление? Почему так?
От: Niswn  
Дата: 03.07.08 14:15
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Ок. Давай пока по проще. Хорошо, возмем q=2. Тогда получается большая погрешность:

Z>0,9 ~= 9*2^-3 = 9/8 = 1,125
Z>или
Z>0,9 ~= 9*2^-4 = 9/16 = 0,5625

Z>Откуда тогда берется 0,8999999?


Как уже было замечено эта погрешность происходит из-за преобразования чисел. Она заключается в том, что число 0.9 в компьютере хранится в двоичном виде, а не в десятичном, а вводишь ты его в десятичном. Попробуй преобразовать число 0.9 в двоичный вид, затем преобразовать обратно в десятичный, и ты увидишь, что это итоговое число будет близко к 0.9, но оно не будет ему равно, и величина этой погрешности зависит от количества знаков, которое занимает мантисса: чем больше разрядность мантиссы, тем меньше погрешность.
ICQ: 326084932
Re[11]: Округление? Почему так?
От: zbanned  
Дата: 03.07.08 15:14
Оценка:
Здравствуйте, Niswn, Вы писали:

N>Здравствуйте, zbanned, Вы писали:


Z>>Ок. Давай пока по проще. Хорошо, возмем q=2. Тогда получается большая погрешность:

Z>>0,9 ~= 9*2^-3 = 9/8 = 1,125
Z>>или
Z>>0,9 ~= 9*2^-4 = 9/16 = 0,5625

Z>>Откуда тогда берется 0,8999999?


N>Как уже было замечено эта погрешность происходит из-за преобразования чисел. Она заключается в том, что число 0.9 в компьютере хранится в двоичном виде, а не в десятичном, а вводишь ты его в десятичном. Попробуй преобразовать число 0.9 в двоичный вид, затем преобразовать обратно в десятичный, и ты увидишь, что это итоговое число будет близко к 0.9, но оно не будет ему равно, и величина этой погрешности зависит от количества знаков, которое занимает мантисса: чем больше разрядность мантиссы, тем меньше погрешность.


Как уже сказали на 4 сообщения выше — ошибка из-за преобразования и я это сразу же понял после первого поста.
Я уже два раза преобразовал и в двоичном виде, и в десятичном, все равно не нашел момента где у нас теряется эта маленькая величина. Если мы разложим 0,9 как aq^n при q = 2 то у нас может быть только два варианта:
0,9 ~= 9*2^-3 = 9/8 = 1,125
или
0,9 ~= 9*2^-4 = 9/16 = 0,5625
Ни один из них не дает нам ни первоначальный 0.9, ни сишный 0,8999999.

Помогите правильно преобразовать 0,9 в формат с плавающей запятой, я был бы очень рад.
Re[12]: Округление? Почему так?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.07.08 15:52
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Помогите правильно преобразовать 0,9 в формат с плавающей запятой, я был бы очень рад.


http://en.wikipedia.org/wiki/IEEE_754-1985
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
AVK Blog
Re[12]: Округление? Почему так?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 03.07.08 16:01
Оценка: 1 (1)
Здравствуйте, zbanned, Вы писали:

Z>Как уже сказали на 4 сообщения выше — ошибка из-за преобразования и я это сразу же понял после первого поста.

Z>Я уже два раза преобразовал и в двоичном виде, и в десятичном, все равно не нашел момента где у нас теряется эта маленькая величина. Если мы разложим 0,9 как aq^n при q = 2 то у нас может быть только два варианта:
Z>0,9 ~= 9*2^-3 = 9/8 = 1,125
Z>или
Z>0,9 ~= 9*2^-4 = 9/16 = 0,5625
Z>Ни один из них не дает нам ни первоначальный 0.9, ни сишный 0,8999999.
Какую-то ересь написал.

Z>Помогите правильно преобразовать 0,9 в формат с плавающей запятой, я был бы очень рад.


Мантисса вещественных чисел хранятся как разложение по 2^(-n).
0.9 — 2^(-1) = 0.9-0.5 = 0.4 в результат уходит 1
0.4 — 2^(-2) = 0.4 — 0.25 = 0.15 в результат уходит 1
0.15 — 2^(-3) = 0.15 — 0.125 = 0.025 в результат уходит 1
0.025 < 2^(-4) в результат уходит 0
0.025 < 2^(-5) в результат уходит 0
0.025 — 2^(-6) = 0.025 — 0.015625 = 0.009375 в результат уходит 1
...

Так можно продолжать долго, результат не станет нулевым. Тем не менее бит на мантиссу у нас ограниченное количество, поэтому возникают ошибки представления чисел с плавающей точкой и 0.9 на самом деле равно 0.89999999
Re[12]: Округление? Почему так?
От: Niswn  
Дата: 03.07.08 16:46
Оценка: 13 (3)
Здравствуйте, zbanned, Вы писали:
Z>Как уже сказали на 4 сообщения выше — ошибка из-за преобразования и я это сразу же понял после первого поста.
Z>Я уже два раза преобразовал и в двоичном виде, и в десятичном, все равно не нашел момента где у нас теряется эта маленькая величина. Если мы разложим 0,9 как aq^n при q = 2 то у нас может быть только два варианта:
Z>0,9 ~= 9*2^-3 = 9/8 = 1,125
Z>или
Z>0,9 ~= 9*2^-4 = 9/16 = 0,5625
Z>Ни один из них не дает нам ни первоначальный 0.9, ни сишный 0,8999999.

Z>Помогите правильно преобразовать 0,9 в формат с плавающей запятой, я был бы очень рад.


Хорошо. Поясню на примере. gandjustas уже написал один пример, но нас учили немного по другому переводить число в двоичное, поэтому это может будет кому интересно (хотя нас этому учили на информатике в школе).
число 9 = 1001 (это надеюсь понятно)
число 0.9 НЕ РАВНО 0.1001
Нас учили так преобразовывать дроби:
1. берем число от 0 до 1.
2. умножаем его на два.
3. если число больше еденицы, то вычитаем еденицу (в результате опять получится число 0..1). И записываем 1 как i-тую цифру после запятой двоичного числа.
4. если число после умножение меньше еденицы, то ничего не делаем. И записываем 0 как i-тую цифру после запятой двоичного числа.
5. повторяем шаги 2-4 пока в результате не получится ноль.

0.9*2=1.8 => 0.8*2=1.6 => 0.6*2=1.2 => 0.2*2=0.4 => 0.4*2=0.8 => 0.8*2=1.6 => 0.6 и т.д.
      1            1            1            0            0            1

получаем двоичное число 0.111001.....
Это можно продолжаться до бесконечности, а мантисса у нас ограничена. отсюда и появляется погрешность. чем больше двоичное число будет содержать разрядов после запятой (а это определяется разрядностью мантиссы), тем меньше будет погрешность.
К примеру, число 0.75 сводится в ноль за 2 шага и в двоичном виде будет выглядеть как 0.11. Поэтому оно не будет иметь погрешность при преобразовании.
ICQ: 326084932
Re[13]: Округление? Почему так?
От: zbanned  
Дата: 03.07.08 17:24
Оценка:
N>
N>0.9*2=1.8 => 0.8*2=1.6 => 0.6*2=1.2 => 0.2*2=0.4 => 0.4*2=0.8 => 0.8*2=1.6 => 0.6 и т.д.
N>      1            1            1            0            0            1
N>


Народ, спасибо всем огромное за ответы! Все дошло до меня

Один вопрос остался: почему пошли именно таким путем деления? Почему бы не представить мантиссу 9 просто как 1001 (без деления)?
Т.е. можно же 32 битное число для плавающей запятой, просто разделить на две части на 16 и 16 бит и упаковать туда мантиссу со степенью, например так:
16 бит под мантиссу (знаковое 16-битное целое);
16 бит под степень (тоже знаковое 16-битное целое);

В итоге можно 0.9 представилось бы как
0000 0000 0000 1001 под мантиссу и
1111 1111 1111 1111 под степень в двоичном виде.

А число 9 представилось бы как:
0000 0000 0000 1001 под мантиссу и
0000 0000 0000 0000 под степень в двоичном виде.

И все считать по основанию 10ой системы. Для чего понадобилось таким хитрым способом переводить мантиссу делением?
Re[14]: Округление? Почему так?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.07.08 17:26
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Один вопрос остался: почему пошли именно таким путем деления? Почему бы не представить мантиссу 9 просто как 1001 (без деления)?


Аппаратная арифметика при чисто двоичном представлении проще. В 85 году, когда был выпущен IEEE 754, это было крайне важно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
AVK Blog
Re[14]: Округление? Почему так?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 03.07.08 18:21
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Один вопрос остался: почему пошли именно таким путем деления? Почему бы не представить мантиссу 9 просто как 1001 (без деления)?

Z>Т.е. можно же 32 битное число для плавающей запятой, просто разделить на две части на 16 и 16 бит и упаковать туда мантиссу со степенью, например так:
Z>16 бит под мантиссу (знаковое 16-битное целое);
Z>16 бит под степень (тоже знаковое 16-битное целое);

Z>В итоге можно 0.9 представилось бы как

Z>0000 0000 0000 1001 под мантиссу и
Z>1111 1111 1111 1111 под степень в двоичном виде.

Z>А число 9 представилось бы как:

Z>0000 0000 0000 1001 под мантиссу и
Z>0000 0000 0000 0000 под степень в двоичном виде.
И получили бы точность в 5 десятичных знаков. Сейчас бы еще больше плевались при таком раскладе.

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

Z>И все считать по основанию 10ой системы. Для чего понадобилось таким хитрым способом переводить мантиссу делением?

1)Однозначность хранимого числа
2)Оптимальность хранения
3)Быстродействие арифметических операций
Re[14]: Округление? Почему так?
От: Овощ http://www.google.com
Дата: 04.07.08 06:13
Оценка:
Здравствуйте, zbanned, Вы писали:

Z>Один вопрос остался: почему пошли именно таким путем деления? Почему бы не представить мантиссу 9 просто как 1001 (без деления)?

Z>Т.е. можно же 32 битное число для плавающей запятой, просто разделить на две части на 16 и 16 бит и упаковать туда мантиссу со степенью, например так:
Z>16 бит под мантиссу (знаковое 16-битное целое);
Z>16 бит под степень (тоже знаковое 16-битное целое);

Z>В итоге можно 0.9 представилось бы как

Z>0000 0000 0000 1001 под мантиссу и
Z>1111 1111 1111 1111 под степень в двоичном виде.

Z>А число 9 представилось бы как:

Z>0000 0000 0000 1001 под мантиссу и
Z>0000 0000 0000 0000 под степень в двоичном виде.

Z>И все считать по основанию 10ой системы. Для чего понадобилось таким хитрым способом переводить мантиссу делением?


Вот именно так и работает тип System.Decimal

A decimal number is a floating-point value that consists of a sign, a numeric value where each digit in the value ranges from 0 to 9, and a scaling factor that indicates the position of a floating decimal point that separates the integral and fractional parts of the numeric value.

The binary representation of a Decimal value consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the 96-bit integer and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28. Therefore, the binary representation of a Decimal value is of the form, ((-2^96 to 2^96) / 10^(0 to 28)), where -2^96-1 is equal to MinValue, and 2^96-1 is equal to MaxValue.

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.