double-арифметика или как компенсировать неточности
От: stbzh  
Дата: 28.12.10 17:35
Оценка:
В функцию приходит большое double число. С некой целью, данное число умножается на 0.1, в результате чего могут проявляться эффекты того, что числа с плавающей запятой не могут быть представлены абсолютно точно. А именно, в младших разрядах могут появляться значения отличные от нуля. Например:
Было: d = 123456789012345.0;
Делаем: d *= 0.1;
Получаем: d = 12345678901234.5001;
Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.
Re: double-арифметика или как компенсировать неточности
От: rusted Беларусь  
Дата: 28.12.10 17:47
Оценка: 1 (1) +1
Здравствуйте, stbzh, Вы писали:

в таком случае стоит подумать о замене double на фиксированную точку.
Re: double-арифметика или как компенсировать неточности
От: Сергей Мухин Россия  
Дата: 28.12.10 18:40
Оценка: 1 (1)
Здравствуйте, stbzh, Вы писали:

S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.


для intel процессоров, для плавающей точки есть битик: только точные вычисления. Если его выставить, то гарантированно только они и будут, а иначе — исключение. Но почему то мне кажется, что это не тот резудьтат, который Вы хотели получить

А так, Вы описали свойство чисел с фикс точкой, а не плавающей. Ими и пользуйтесь. Как уже и сказали о казывается.
---
С уважением,
Сергей Мухин
Re: double-арифметика или как компенсировать неточности
От: AlexPN  
Дата: 28.12.10 18:42
Оценка: 1 (1)
Здравствуйте, stbzh, Вы писали:

S>...


Арифметика типа double подобной гарантии обеспечить не может. Какова исходная задача? Решение в общем случае --- переход на длинную арифметику или, как посоветовали, на фиксированную точку.
Re: double-арифметика или как компенсировать неточности
От: Vain Россия google.ru
Дата: 28.12.10 22:43
Оценка: 1 (1)
Здравствуйте, stbzh, Вы писали:

S>В функцию приходит большое double число. С некой целью, данное число умножается на 0.1, в результате чего могут проявляться эффекты того, что числа с плавающей запятой не могут быть представлены абсолютно точно. А именно, в младших разрядах могут появляться значения отличные от нуля. Например:

S>Было: d = 123456789012345.0;
S>Делаем: d *= 0.1;
S>Получаем: d = 12345678901234.5001;
S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.
Это можно гарантировать только с определённой точностью, т.е. с артефактами.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: double-арифметика или как компенсировать неточности
От: Erop Россия  
Дата: 29.12.10 00:14
Оценка: 1 (1)
Здравствуйте, stbzh, Вы писали:

S>Получаем: d = 12345678901234.5001;

S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.
К сожалению, это невозможно. Многие числа непредставимы в double точно. Ну, скажем число 0.1, например...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: double-арифметика или как компенсировать неточности
От: TimurSPB Интернет  
Дата: 29.12.10 00:17
Оценка:
Здравствуйте, stbzh, Вы писали:

S>В функцию приходит большое double число.

В каком смысле большое?
Make flame.politics Great Again!
Re: double-арифметика или как компенсировать неточности
От: stbzh  
Дата: 29.12.10 10:17
Оценка:
S>в таком случае стоит подумать о замене double на фиксированную точку
— Спасибо, обязательно посмотрю в этом направлении. Никто не даст переписать мне всю логику (я занимаюсь только bugfix, а это уже серьезная переделка enchancement), но может быть мне удасться внутри этой функции преобразовать double к представлению с фиксированной точкой, сместить разряд и сделать обратное преобразование.

S>Решение в общем случае --- переход на длинную арифметику

— Постараюсь найти информацию о "длинной арифметике". Может подскажете проверенный источник или сможете сразу дать мне рекомендацию в данном конкретном случае?

S>К сожалению, это невозможно. Многие числа непредставимы в double точно. Ну, скажем число 0.1, например...

— это то я знаю. хотелось бы как то обмануть. возможно не умножать на 0.1, а, к примеру, делить на 10. Или сделать что то типа такого трюка:
double original = 123456789012345.0;
double result = original / 10; // появляются артефакты 12345678901234.5001
double copy = result * 10; // тут слабое место - если я правильно думаю, здесь могут быть свои артефакты, например 123456789012345.0012
result -= (copy - original); // если бы умножение на 10 не добавляло своих, то можно просто вычесть ошибку привнесенную делением


S>В каком смысле большое?

— Числа порядка приведенных выше. Например ошибка стабильно повторяется именно с числом 123456789012345.0
Re[2]: double-арифметика или как компенсировать неточности
От: Erop Россия  
Дата: 29.12.10 11:56
Оценка: 1 (1) +2
Здравствуйте, stbzh, Вы писали:

S>- Спасибо, обязательно посмотрю в этом направлении. Никто не даст переписать мне всю логику (я занимаюсь только bugfix, а это уже серьезная переделка enchancement), но может быть мне удасться внутри этой функции преобразовать double к представлению с фиксированной точкой, сместить разряд и сделать обратное преобразование.


Это не поможет!!!

S>
S>double original = 123456789012345.0;
S>double result = original / 10; // появляются артефакты 12345678901234.5001
S>double copy = result * 10; // тут слабое место - если я правильно думаю, здесь могут быть свои артефакты, например 123456789012345.0012
S>result -= (copy - original); // если бы умножение на 10 не добавляло своих, то можно просто вычесть ошибку привнесенную делением
S>


Это тоже не поможет!

Проблема глубже.
Вот представь, что к тебе придёт число 123456789012341, и тебе надо будет получить в дабле число 12345678901234.1, а оно в дабле непредставимо!
Там же двоичный порядок, так что и дробь двоичная.
А 1/5 или 1/10 нельзя представить точно в виде конечной двоичной дроби по тем же причинам, по которым нельзя в десятичной представить 1/7 или 1/70...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: double-арифметика или как компенсировать неточности
От: Vain Россия google.ru
Дата: 29.12.10 14:32
Оценка: :)
Здравствуйте, stbzh, Вы писали:

S>В функцию приходит большое double число. С некой целью, данное число умножается на 0.1, в результате чего могут проявляться эффекты того, что числа с плавающей запятой не могут быть представлены абсолютно точно. А именно, в младших разрядах могут появляться значения отличные от нуля. Например:

S>Было: d = 123456789012345.0;
S>Делаем: d *= 0.1;
S>Получаем: d = 12345678901234.5001;
S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.
Самого главного никто не спросил, а что за компилятор? Msvc2005 показывает 12345678901234.500000 как надо.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: double-арифметика или как компенсировать неточности
От: Сергей Мухин Россия  
Дата: 29.12.10 14:58
Оценка:
Здравствуйте, Vain, Вы писали:


V>Самого главного никто не спросил, а что за компилятор? Msvc2005 показывает 12345678901234.500000 как надо.


показывает — это немного не то, что есть на самом деле
более того зависит и от опций компилятора и от настройки (_controlfp_s)
---
С уважением,
Сергей Мухин
Re[3]: double-арифметика или как компенсировать неточности
От: Vain Россия google.ru
Дата: 29.12.10 15:17
Оценка:
Здравствуйте, Сергей Мухин, Вы писали:

V>>Самого главного никто не спросил, а что за компилятор? Msvc2005 показывает 12345678901234.500000 как надо.

СМ>показывает — это немного не то, что есть на самом деле
СМ>более того зависит и от опций компилятора и от настройки (_controlfp_s)
Проверял, 12345678901234.5001 — не получается.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: double-арифметика или как компенсировать неточности
От: pagid Россия  
Дата: 29.12.10 16:26
Оценка: +1
Здравствуйте, Vain, Вы писали:

V>Самого главного никто не спросил, а что за компилятор? Msvc2005 показывает 12345678901234.500000 как надо.


Возьми d = 12345678901234.0;
d *= 0.1;
получишь d = 1234567890123.4001

эффект несомненно существует, правда что и с какой целью непонятно хочет добиться ТС непонятно.
Re[2]: double-арифметика или как компенсировать неточности
От: AlexPN  
Дата: 29.12.10 16:40
Оценка: 3 (1)
Здравствуйте, stbzh, Вы писали:

S>>Решение в общем случае --- переход на длинную арифметику

S>- Постараюсь найти информацию о "длинной арифметике". Может подскажете проверенный источник или сможете сразу дать мне рекомендацию в данном конкретном случае?

Можно банально начать с википедии: Arbitrary-precision_arithmetic. Там же есть и библиотеки для языков программирования: Libraries
Re[2]: double-арифметика или как компенсировать неточности
От: TimurSPB Интернет  
Дата: 29.12.10 16:42
Оценка:
Не только от компилятора, но и от процессора может зависеть.
Make flame.politics Great Again!
Re: double-арифметика или как компенсировать неточности
От: roman313  
Дата: 07.01.11 19:53
Оценка:
А у меня результат ожидаемый,
= 12345678901234.500
VC++ 2010


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


S>В функцию приходит большое double число. С некой целью, данное число умножается на 0.1, в результате чего могут проявляться эффекты того, что числа с плавающей запятой не могут быть представлены абсолютно точно. А именно, в младших разрядах могут появляться значения отличные от нуля. Например:

S>Было: d = 123456789012345.0;
S>Делаем: d *= 0.1;
S>Получаем: d = 12345678901234.5001;
S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.
Re[2]: double-арифметика или как компенсировать неточности
От: bkat  
Дата: 07.01.11 19:59
Оценка:
Здравствуйте, Vain, Вы писали:

V>Самого главного никто не спросил, а что за компилятор?


Это как раз далеко не самое главное.
Re[2]: double-арифметика или как компенсировать неточности
От: Shellac  
Дата: 07.01.11 22:38
Оценка:
Здравствуйте, rusted, Вы писали:

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


R>в таком случае стоит подумать о замене double на фиксированную точку.


а какие есть типы для работы с фиксированной точкой?
Re[3]: double-арифметика или как компенсировать неточности
От: sraider http://dvinogradov.blogspot.com
Дата: 11.01.11 13:27
Оценка:
R>>в таком случае стоит подумать о замене double на фиксированную точку.
S>а какие есть типы для работы с фиксированной точкой?

int, long, __int64, etc.
просто держи позицию точки в уме
Re: double-арифметика или как компенсировать неточности
От: Dmi3S Россия http://dmi3s.blogspot.com/
Дата: 13.01.11 19:23
Оценка:
Здравствуйте, stbzh, Вы писали:

S>В функцию приходит большое double число.

S>Мне необходимо каким то образом гарантировать, что десятичное представление числа будет оставаться таким же, то есть грубо говоря нужно перенести запятую в десятичном представлении, без привнесения артефактов.

Я бы для начала попробовал обойтись использованием извращенной формы fixed-point representation в виде std::pair<double,int>, которая хранила бы изначальное значение и делитель. Т.е. для этого случая хранил бы std::make_pair(1234567890.5,10). Для расчетов можно написать функцию получения double: double get_value( const std::pair<double,int> pair ) { return pair.first/pair.second; }. Ну а для преобразование в строку получить представление pair.first, и уже в string-е передвинуть десятичную точку. Для одного-единственного использования внутри одной-единственной функции может прокатить.

PS. Вообще же говоря, в функцию должно приходить не-double число.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.