Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
Как обойти? ------------------ Багавудин Мирзаев |
Re: Почему округляет? | |
---|---|
andrewk Сообщений: 174 Откуда: Красноярск Дата регистрации: 15.05.2005 |
Потому что Фокс работает с 15-ю значащими цифрами. Насколько помню, это связано с внутренним представлением чисел с плавающей точкой. Не Фоксовым, а принципиально компьютерным. Для большей детализации нужно подключать цифровой сопроцессор, а это другая песня. Всё это говорю по памяти многолетней давности, возможно, что-то не очень так.
В твоём примере, если не считать десятичную точку, то 15 цифр будет "819338974300.043". Но после "3" идёт "9", поэтому округляет вверх. Как обойти не разбирался, поскольку не было нужно. |
Re: Почему округляет? | |
---|---|
Crispy Сообщений: 18571 Дата регистрации: 16.05.2005 |
А для каких целей нужно? Для каждой ситуации можно придумать какой-то свой способ, наиболее подходящий. ------------------ В действительности все иначе, чем на самом деле. (Антуан де Сент-Экзюпери) |
Re: Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
Спасибо, что напомнили.
Я, конечно, для своей цели вышел из положения.
------------------ Багавудин Мирзаев |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Потому что таков предел точности для вещественных чисел в фоксе (да и в большинстве других систем тоже). msdn.microsoft.com Цитата: Использовать другой тип данных - например Currency позволяет хранить 19 десятичных знаков (кроме "самых больших" 19-значных - в хелпе чётко прописан диапазон значений для этого типа) без потери точности (но имеет строго фиксированную позицию десятичной точки). Иначе - искать библиотеки работы с числами произвольной точности. ------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Канешна, именно так
------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
vic7tar Сообщений: 48 Дата регистрации: 27.02.2017 |
И что этот код означает? У меня выдал 8193389743000435. |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
То, что автор темы занимается ерундой, вместо того чтобы прочесть и понять/осознать документацию.
------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
vic7tar Сообщений: 48 Дата регистрации: 27.02.2017 |
Месяц назад попадалось,что у всех версий, кроме 6, существует баг с этими округлениями.
Кто поправит? |
Re: Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
И все-таки. Что здесь происходит?
Пробовал и другие значения, некоторые меняются, а некоторые нет. Разобраться в какой последовательности это происходит тоже не представляется возможным. Это с чем едят? ------------------ Багавудин Мирзаев |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
То что и написано выше - потеря точности для вещественных чисел. Не может в стандартном double формате хранится без потерь 16- и более -значные десятичные числа. Под мантиссу (собственно "цифры") формат отводит 52 бита, плюс один неявный, итого 53 бита - что даёт место для хранения ~15.95 десятичных знаков. На практике это значит что некоторые десятичные числа из 16-ти цифр будут в этом формате терять точность в младшем знаке - что и видно из всех приводимых примеров. При этом имеют значение именно сами знаки, а вовсе не порядок числа. Т.е. с равной частотой будет "дёргаться" как 4-я цифра после запятой в числе из 12 знаков в целой части (и соответственно 4-х в дробной), так и просто младшая цифра целого числа из 16-ти цифр. И это НЕ "округление". Естественно. Поэтому всё просто - НЕ преобразуй его в "просто число". Ни явно через MTON() ни неявно любым другим способом. В строку его тоже нужно преобразовывать крайне аккуратно. В частности функция STR работает с numeric (он же double) а не с currency - потому и проявляется ровно та же самая проблема. Это на самом деле очень просто - достаточно посмотреть на бинарное представление числа (точнее "соседних" чисел). Только практического смысла в этом нет никакого. Ну и что с того что ты знаешь какие именно числа так себя ведут? Чем это тебе поможет? ru.wikipedia.org Глупости. Во-первых это не баг, а ШТАТНОЕ поведение. Оно присутствует на всех системах использующих IEEE-ный формат представления вещественных чисел (тот же excel) - ссылка в фоксовом хелпе как раз указывает на MSKB статью описывающую сие поведение для эксела. Во-вторых это поведение невозможно изменить. Просто разные системы чуть по разному "визуализируют" бинарную форму вещественного числа, и поэтому вполне может быть так что в одной системе (скажем excel) "неправильно" отображается число 819338974300.0434, а в другой (например VFP6) неправильно будет отображаться какое-нить 819338974300.0432 Вот и вся разница. Ни одна система оперирующая double форматом не в состоянии корректно хранить и отображать ВСЕ возможные вещественные числа из 16-ти цифр. Уж не говоря о 17-й и последующих, которые вообще ВСЕГДА теряются. ------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
vic7tar Сообщений: 48 Дата регистрации: 27.02.2017 |
А это тоже штатное поведение?
|
Re: Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
Попробовал так
Проверил все возможные значения после запятой от 0000 до 9999 Вроде все правильно, если проверяю правильно. ------------------ Багавудин Мирзаев |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Да, вполне, хотя и выглядит странно. В бинарном виде в формате double НЕ БЫВАЕТ числа "точно 512.925"
В фоксе есть дополнительная "нечеловеческая" логика, служащая для получения "красивых" результатов из вот таких "некрасивых" чисел. Внутренне фокс помимо самого числа хранит и его "визуальные атрибуты" - сколько десятичных знаков всего, сколько после запятой - и использует эти атрибуты при некоторых видах вычислений (поэтому, в частности ? 5*10 и ? 5.0*10.0000 выглядят на экране совсем уж по разному, хотя данные числа и представлены в памяти одинаково и "совершенно точно", без погрешностей и каких либо "округлений"). К сожалению, помимо полезной работы она же и приводит к подобным "глюкам" с тем же ROUND(). ------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
2 Baga
? TRANSFORM(nLow, "9999999999999.9999") Хотя я не очень понимаю для чего нужно переводить это число в строку, да ещё и десятичную точку потом выбрасывать. Есть такое подозрение что это число, вовсе не число (а какой-то шифр/код), и потому его банально НЕ НУЖНО нигде и никогда переводить из строкового вида ни в number ни в currency... ------------------ WBR, Igor |
Re: Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
Вы абсолютно правы. Это из штрих-кода получаю данные в currency, чтобы не потерять точность, так как заранее известно, что результатом должно быть символьное выражение, состоящее из 15 или 16-ти числовых знаков. Не преобразуя результат в символьную строку, невозможно его использовать, так как в штрих-коде может быть зашифровано символьное выражение в 15 заков и тогда его нужно дополнить ведущим нулем. Если бы результат не принимал бы значение в 16 символов, то и незачем было бы строить эту чехарду. Но результат может принимать 16-значное символьное выражение и без ведущего нуля, как мы рассматривали выше, и в 15 знаков, как, например, в примере ниже:
------------------ Багавудин Мирзаев Исправлено 2 раз(а). Последнее : Baga, 11.07.17 17:11 |
Re: Почему округляет? | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Начнём с того, что 8 байт могут трактоваться как знаковое (signed) так и беззнаковое (unsigned) "очень длинное целое" (long long). При этом и записываться в памяти они могут как в прямом (big-endian), так и в обратном порядке (little-endian). Оба эти вопроса (в каком виде принято соответствующее "число") следует выяснить ДО того как что либо с ним делать.
Да, фоксовый тип currency внутренне хранится как целое 8-байтное число со знаком (т.е. как раз long long), что и позволяет использовать его для целей "визуализации" такого рода чисел - конечно, с некоторыми неудобствами (фиксированная точка после 4-х младших десятичных разрядов из их числа). Твой код: - Предполагает big-endian порядок записи байт (т.е. строка 0h0000000000000001 это именно 1, а не 72057594037927936 - т.е. 1*16^14). Впрочем, этот нюанс урегулируется флажком "R" при конвертации "строки" в currency. - Предполагает что число записано как ЗНАКОВОЕ (т.е. строка 0hFFFFFFFFFFFFFFFF это -1 а не 18446744073709551615). К сожалению этот нюанс изменить НЕЛЬЗЯ. Флаг "S" просто отключает ненужное "переворачивание" старшего бита во входном массиве перед его "конвертацией" в currency. Так чтобы строка 0h0000000000000001 не превратилась в строку 0h8000000000000001 которую фокс будет считать отрицательным числом, а конкретно -9223372036854775807. Заставить фокс трактовать эти байты как unsigned long long нельзя. - Не встретится строка 0h8000000000000000 которая формально является числом -9223372036854775808 но, к сожалению, не обрабатывается фоксом. Плюс к тому, максимальная длина строкового представления для значений типа currency составляет 21 символ, а не 18 как у тебя в шаблоне. Формат должен быть "9999999999999999.9999" - это 19 цифр, десятичная точка и место под минус. Извращаться с ALLTRIM и PADL совершенно не обязательно - в форматную строку можно прописать модификатор заполняющий ведущие позиции нулями "@L 9999999999999.9999". Только учти, что для положительных чисел ведущий ноль ВСЕГДА будет выводится - даже для "максимального" числа 922337203685477.5807, тогда как для отрицательных на его месте будет знак минус. Имеет ли TRANSFORM() с указанной маской какие-либо проблемы при конвертации currency в строки я не в курсе. Я не работал с этим типом, и тем более не пытался через него получать "десятичное" представление long long чисел P.S. Если гарантировано то что "результатом должно быть символьное выражение, состоящее из 15 или 16-ти числовых знаков", то в общем и целом код пойдёт - т.к. это заметно меньше диапазона даже "знакового" long long, т.е. на отрицательные числа мы никогда не будем попадать. Ну и размер маски вполне можно уменьшить (16 девяток и одна точка) - чтобы после удаления точки получать гарантировано 16 десятичных цифр (с ведущими нулями если включать @L, естественно). Если "вдруг" попадётся последовательность байт представляющая собой 17 или более значное long long, или 16-значное отрицательное число, то TRANSFORM выдаст кучу звёздочек, так что эту "ошибку в данных" легко будет обнаружить (для других отрицательных чисел "переполнения" не будет, и их можно просто проверять по первому символу полученной строки - там будет "-"). ------------------ WBR, Igor Исправлено 1 раз(а). Последнее : Igor Korolyov, 11.07.17 18:42 |
Re: Почему округляет? | |
---|---|
Baga Автор Сообщений: 540 Откуда: г. Махачкала Дата регистрации: 03.06.2006 |
Огромное спасибо за столь поучительный пост. Надеюсь он будет полезен не только мне.
------------------ Багавудин Мирзаев |
© 2000-2024 Fox Club  |