|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Отследить факт потери точности при присвоении значения переменной типа Currency
Приветствую уважаемых гуру, и, вообще, всех участников данного форума, который уже очень-очень много раз спасал в трудных ситуациях. Ну, и, как говорится "никогда не было, и, вот, опять...". Снова заносит меня в области, где Гугл не помогает (хоть, вроде, меня там пока не забанили, и искать честно пытался). Суть вопроса. Все мы знаем, что переменной типа Currency (с фиксированной точностью в 4 знака после запятой) можно присвоить значение и с бо́льшим количеством знаков, и никакой ошибки не будет. Если, конечно, не считать того, что в этой переменной будет храниться уже не то число, которое мы присваивали. Так, в частности, код, представленный ниже, отработает без ошибок, без варнингов и хинтов: Код:
var Значение: Currency; begin Значение := 0.56787; end; Однако, в переменной "Значение" будет храниться в точности 0,5679. В настройках проекта у меня стоят галочки проверки переполнения и выхода за границы диапазона, но что толку... Существует ли какая-то возможность (например, путем установки каких-либо директив компилятора, или кто его знает, как...) отловить момент потери точности при присвоении значения переменной типа Currency? Может быть, как-то можно переопределить оператор присваивания для конкретного случая (если слева Currency, а справа Single, Real, Double или Extended)? Собственно, вопрос сформулирован. Дальше уже можно и не читать (ниже — просто описание моих тщетных поисков путей решения). (Предвижу вопрос, типа, "... а нафига оно тебе?". Отвечу сразу: во-первых, академический интерес, во-вторых, приложение связано с подсчетом финансовых показателей, и если из-за потерь точности где-то вылезут какие-то ошибки, то я хотел бы иметь возможность, хотя бы логировать те моменты, где происходит эта потеря, чтобы, если что, потом знать, где копать). Григорьева А. Б. "О чём не пишут в книгах по Delphi" (глава 3. "Подводные камни") читал внимательно. Но после прочтения стало не спокойнее, а, напротив, еще тревожнее на душе, поскольку до этого я и сам знал, что с плавающей запятой все сложно, а тут еще и осознал, "размеры бедствия всерьез". Что же касается ответа на вопрос "Как же быть?", то он пишет так: Цитата:
... но, как бы... от такого "доброго совета" не легче ни разу (от слова "совсем"). Ну, вот, использую я Currency, и точно знаю, что у меня не плавающая, а фиксированная точка (железно 4 знака, и "абсолютная точность"). Но, я даже не могу никак отследить момент типа: Код:
var Значение: Currency; begin Значение := Что_то_там / Что_то_ещё; end; и даже знать не знаю, сколько я потерял при таком присвоении. Пытался запилить свой класс TCurrencyExt, в котором, среди прочих, реализовал такой метод: Код:
procedure TCurrencyExt.SetValue(const AValue: Extended); begin FValue := AValue; if AValue <> FValue then АЛАРМ_АЛАРМ_БИТЬ_ВО_ВСЕ_КОЛОКОЛА_ПОТЕРЯ_ТОЧНОСТИ!!!11; end; Ну, ежу понятно, что если вызывать его так: Код:
var ... CExt: TCurrencyExt; ... implementation procedure TViewMain.FormCreate(Sender: TObject); var Значение: Currency; begin CExt := TCurrencyExt.Create(...); Значение := 0.56787; CExt.SetValue(Значение); end; ... то толку с этого как с козла молока. Единственное, когда это может спасти, это, например, если: Код:
var ... CExt: TCurrencyExt; ... implementation procedure TViewMain.FormCreate(Sender: TObject); begin CExt := TCurrencyExt.Create(...); CExt.SetValue(0.56787); end; или Код:
var ... CExt: TCurrencyExt; ... implementation procedure TViewMain.FormCreate(Sender: TObject); begin CExt := TCurrencyExt.Create(...); CExt.SetValue(StrToFloat(Значение_какого_нибудь_там_Edit_а)); end; Помогите, пожалуйста, если кто-нибудь может... Разрабатываю генератор случайных чисел на основе пинга (информация — на сайте http://pbrng.16mb.com/) Последний раз редактировалось mxustin, 19.10.2019 в 21:11. |
#2
|
||||
|
||||
Интересная конечно теория, но справка говорит что данный формат может содержать только 4 знака после запятой. Может правильнее взять Single или Double?
Жизнь такова какова она есть и больше никакова. Помогаю за спасибо. |
#3
|
|||
|
|||
Напрямую отследить это не получится, т.к. здесь нет переполнения или его то подобного. Тут происходит приведение совместимых типов. Т.е. если тебе надо больше чем 4 цифры после точки, то надо писать свой тип.
Если по простому. то можно просто сделать свою арифметику, например, на основе Int64. Пусть тебе надо 6 цифр после точки. Тогда для при присвоении значения умножаешь его на 1000000, а при извлечении делишь на то же значение. Тогда вся остальная арифметика остается такой же, просто по факту происходит переход к полностью целочисленной арифметике. Более сложный вариант - создание класса в котором целая и дробна части хранятся в разных целочисленных полях. Ну и реализация всей арифметике на тебе. |