|
v7: 1SQlite возвращает число 44.6599999999999 вместо 44.66 | ☑ | ||
---|---|---|---|---|
0
zelenprog
09.06.24
✎
18:10
|
Здравствуйте!
Есть вот такой запрос по табличной части документа:
В некоторых строках числовые значения (Цена, Сумма, СуммаНДС) возвращаются с длинными десятичными дробями. Например, вместо суммы НДС 44,66 руб. в результат запроса попадает число 44.6599999999999. При этом в самом документе в этой строке ТЧ стоит сумма НДС 44,66. Из-за чего это может быть? В конфигураторе указан формат реквизита "Число", длина = 15, точность = 2. То есть даже принципиально возвращаемое число не может иметь более 2-х знаков после запятой. Как бороться с этой бедой? |
|||
1
Злопчинский
09.06.24
✎
18:12
|
можно в запросе указать типизацию с точностью, он и округлит
|
|||
2
Злопчинский
09.06.24
✎
18:14
|
типа (это для 1Sqlite)
ТекстЗапроса = " |SELECT sum(ЗаявкаПокупателя.Количество) [Количество :Число.15.3] |
|||
3
Djelf
09.06.24
✎
20:56
|
Виноват IEEE-754
|
|||
4
zelenprog
10.06.24
✎
08:30
|
(3) >> Виноват IEEE-754
Объясни плиз конкретнее в чем причина? Это же стандарт. Программы (и 1С77 и 1SQLite) должны его выполнять. Значит, скорее всего дело не в самом стандарте, а в том, что какая-то программа его неправильно обрабатывает. |
|||
5
zelenprog
10.06.24
✎
08:32
|
(1) >> можно в запросе указать типизацию с точностью, он и округлит
Можно, конечно. Но хотелось бы точно знать причину такой ошибки. Зная причину, можно найти более правильное решение. |
|||
6
Eiffil123
10.06.24
✎
11:14
|
(5) наверно sql хранит данные в двоичном формате и при конвертации возникает ошибка. в excel такое тоже случается
|
|||
7
Aleksey
10.06.24
✎
12:34
|
(4) как раз программа его и выполняет. Вот такой вот стандарт, что на выходе дает такой результат
|
|||
8
Djelf
10.06.24
✎
17:51
|
Чтобы понять как работает стандарт нужно его сначала прочитать и понять.
И не бегать с флажками - этот формат амно, верните амно, иначе мы все закидывать тут амном. |
|||
9
zelenprog
11.06.24
✎
13:08
|
(7) Да вроде там все просто в стандарте.
В Википедии хорошее описание. Все таки вряд ли проблема в самом стандарте. В ячейках dbf-файла стоит число "44,66". Скорее всего, sqlite почему-то неверно читает это значение. |
|||
10
Chai Nic
11.06.24
✎
13:12
|
Возможно sqlite работает через интерфейс, который передает числа как float. Я с таким в com/ole сталкивался, когда нормальная конечная десятичная дробь при передаче внезапно становилась периодической из-за потерь точности.
|
|||
11
Chai Nic
11.06.24
✎
13:12
|
Лучше кастуй всё в строку и дальше обрабатывай уже их
|
|||
12
Ёпрст
11.06.24
✎
13:15
|
Орефков давным давно объснял за это на 1cpp, float тама
|
|||
13
Злопчинский
11.06.24
✎
13:21
|
(11) в свое время, когда не знал 1С, был молодым и очень красивым (сейчас просто красивый - вот до чего 1С довела) - помню впал в жуткий ступор, когда обнаружил что в дбф числа хранятся в виде строковой записи...
|
|||
14
Garykom
11.06.24
✎
13:24
|
(9) https://habr.com/ru/articles/309812/
sqlite все правильно считает, по стандарту проблема в двойном преобразовании, смотри примеры в комментах |
|||
15
Garykom
11.06.24
✎
13:24
|
(13) это самый правильный способ
еще и дату так же хранить прекрасно |
|||
16
zelenprog
11.06.24
✎
13:34
|
(12) >> Орефков давным давно объснял за это на 1cpp, float тама
Не нашел. Как примерно тема называется, в которой это обсуждается? |
|||
17
zelenprog
11.06.24
✎
13:47
|
(13) >> обнаружил что в дбф числа хранятся в виде строковой записи
Я сейчас посмотрел dbf-файл: поле имеет тип "Numeric", длина 16, точность 2. Значит, там числовое значение. |
|||
18
Злопчинский
11.06.24
✎
14:01
|
угу, записанное смвольным значением...
|
|||
19
Ёпрст
11.06.24
✎
19:11
|
||||
20
Djelf
12.06.24
✎
06:23
|
Можно подключить decimal Extension https://sqlite.org/floatingpoint.html
Будут больше головной боли, значительно больше! (9) Вот тебе каркулятор, для лучшего понимания: https://www.h-schmidt.net/FloatConverter/IEEE754.html Точность IEEE754 очень высока, вероятность получить +-1 копейку в результате ничтожно мала. Просто типизируйте до нужного вам результата и забудьте. Меня больше забавляют баталии на sqlite в стиле как сравнить +0 c -0, или что больше +NaN или -NaN. А тип Infinity это вообще мрак... Некоторые вещи не решаемы разумными путями, нужно изобретать совершенно новую математику. |
|||
21
Ёпрст
12.06.24
✎
07:12
|
(17) ага, только в sqlite
"В SQLite нет поддержки работы с полями типа numeric. Для числовых полей в SQLite используются либо целые числа, либо double" |
|||
22
Djelf
12.06.24
✎
07:33
|
(21) это я подправил, шк норм как длинное число пролетают, все течет все меняетя...
|
|||
23
zelenprog
12.06.24
✎
13:25
|
(20) >> Просто типизируйте до нужного вам результата и забудьте.
ОК. Спасибо. |
|||
24
zelenprog
12.06.24
✎
13:35
|
И все-таки, мое любопытство не оставляет меня в покое, потому что ситуация не совсем ясна.
Не понятно на каком шаге возникает ошибка. (18) >> dbf-файл: поле имеет тип "Numeric", длина 16, точность 2. >> угу, записанное смвольным значением... (21) >> ага, только в sqlite >> "В SQLite нет поддержки работы с полями типа numeric. Для числовых полей в SQLite используются либо целые числа, либо double" Вот еще попалась такая статья про типы данных в SQLite: "Гибкие типы данных в SQLite" https://antonz.ru/sqlite-types/ SQLite может хранить данные любого типа — вне зависимости от того, какой тип указан на столбце. ... SQLite хранит тип не только на столбце, но и на каждом значении в таблице. Именно поэтому в одном столбце без проблем хранятся значения разных типов. Тип на столбце используется как рекомендация: при вставке SQLite пытается привести значение к рекомендуемому типу, но если не получилось — сохраняет «как есть». Как я понимаю, 1SQLite - это надстройка над SQLite. Тип поля в dbf-файле numeric(16,2) - это "свой" тип 1С-ки. Чтобы прочитать из базы 1С-ки число, 1SQLite должна "знать" форматы, в которых 1С-ка хранит числа. То есть 1SQLite все-таки "знает" и правильно "понимает" форматы 1С-ки. Значит, 1SQLite при чтении дробного числа из этого поля, корректно читает из этого поля значение "44,66". Затем 1SQLite должна это прочитанное значение вставить в "правильном" формате в результат запроса. 1SQLite "понимает", что это значение есть число и преобразовывает его в число 44,66. Так как SQLite умеет работать с типом REAL, то происходит преобразование типа 1С-numeric(16,2) в SQLite-REAL. На этом этапе, мне кажется, ошибка не может возникнуть. То есть скорее всего во "внутреннем" представлении результата запроса SQLite это число хранится неискаженное. Затем 1SQLite должна вернуть результат запроса в 1С-ку в виде таблицы значений. Для этого она снова должна преобразовать число в формат, который понимает 1С-ка: она берет число типа REAL и делает из него ... что? И вот на этом этапе возникает ошибка. Верно я понимаю? |
|||
25
Garykom
12.06.24
✎
14:14
|
(24) >На этом этапе, мне кажется, ошибка не может возникнуть
Вот на этом этапе она и возникает 1SQLite сначала конвертит тип 1С в некий промежуточный тип, а затем в тип REAL SQLite Что за промежуточный тип хз, вероятно double В итоге получается изврат: real(double(numeric(15,2))) |
|||
26
Djelf
12.06.24
✎
15:42
|
Каркулятор, я же давал ссылку на каркулятор...
https://www.h-schmidt.net/FloatConverter/IEEE754.html А внутри у ней сейчас неонка! https://en.wikipedia.org/wiki/Kahan_summation_algorithm И если я еще хоть как-то смогу объяснить дефекты стандарта IEEE754, то алгоритм сложения Kahan/Babushka/Neumaier находится за пределами моего восприятия. |
|||
27
zelenprog
12.06.24
✎
17:51
|
(20) >> Просто типизируйте до нужного вам результата и забудьте.
Кстати, а что типизировать надо? После типизации в запросе, ТаблицаЗначений заполнилась верно. Однако, при выгрузке этой ТЗ во внешний sqlite-файл с помощью "УложитьТЗ", в этом sqlite-файле числа опять записались в виде 44.6599999999999. При выгрузке тоже что-то типизировать надо? А что? "Укладываемую" таблицу значений? Но ведь она уже должна быть типизирована после возврата из запроса. |
|||
28
Злопчинский
12.06.24
✎
18:08
|
(27) ну так вроде же уяснили, что в sqlite нет типа данных numeric
. "а что типизировать надо"... типизируй то что надо, а то что не надо типизировать - не типизируй... ;-) |
|||
29
Djelf
12.06.24
✎
18:40
|
Общий принцип - типизируй все и сразу.
> Однако, при выгрузке этой ТЗ во внешний sqlite-файл с помощью "УложитьТЗ", в этом sqlite-файле числа опять записались в виде 44.6599999999999. Ну ёжкин, кот... Дробные числа преобразуются в IEEE754 и с этим ничего сделать нельзя. Не существует точного представления 44.66 в формате IEEE754, и вчера такого не было и завтра тоже такого не будет. Можно кастовать как строку, если перфекционизм есть и спать не дает... |
|||
30
DrZombi
13.06.24
✎
06:22
|
(29) Да 👍
(27) Переходите на 8.4 там такого нет :) |
|||
31
DrZombi
13.06.24
✎
06:22
|
+(30) к (27) Просто когда вы подумаете про 8.3, уже будет 8.4 :)
|
|||
32
Garykom
13.06.24
✎
07:17
|
Проблема в использовании не правильного инструмента
Если надо читать напрямую dbf-ки (и делать к ним запросы) Для этого есть множество правильных инструментов |
|||
33
Chai Nic
13.06.24
✎
08:18
|
(29) Да, почему-то во "взрослых" языках и библиотеках нет поддержки типа данных "десятичная дробь с фиксированной/переменной точностью". Только убогая плавучка. А вот в 1с или в разных СУБД - есть.
|
|||
34
Гена
13.06.24
✎
08:23
|
Дважды перечитал, но так и не понял, а какая разница как там в кишочках физически работают с числом? Ведь фиолетово как посчитают мне НДС: 44.66 или 44.6599999999999, если я в печатную форму выведу потом рубли и копейки.
|
|||
35
Chai Nic
13.06.24
✎
08:27
|
(30) В восьмерке, кстати, подобное встречается при соединении через COM, там тоже нецелые числа теряют точность при передаче. Помню, я в своё время делал финты типа ЗначениеИзСтрокиВнутр(ВнешняяБаза.ЗначениеВСтрокуВнутр(ВнешнийДокумент.Итог("Сумма"))), чтобы не получить ошибку.
|
|||
36
Chai Nic
13.06.24
✎
08:28
|
(34) А если это например себестоимость единицы при массовом производстве скажем шурупов сотнями тысяч штук?) Тут любое округление опасно.
|
|||
37
Aleksey
13.06.24
✎
08:39
|
(30) 8.4 вышла в декабре 2015 и 1с решила что это тупиковая ветвь и продолжила дальше развивать 8.3
|
|||
38
dmpl
13.06.24
✎
08:52
|
(31) Скорее 9.3 :)
(34) А если 4466000 или 4465999,99999999? |
|||
39
dmpl
13.06.24
✎
08:53
|
(36) У 1С для норматива материальных затрат достаточно 3 знаков после запятой :) И брак у них в целых процентах. Так что не критично.
|
|||
40
Chai Nic
13.06.24
✎
08:58
|
(39) Вы про типовую, а я про нетленку, где возможно всякое)
|
|||
41
Djelf
13.06.24
✎
09:12
|
(33) Скорость, скорость, и еще раз скорость.
С 10 ричной системой, которая сделана на основе пальцев, особой скорости не получается достигнуть. (36) Ты не совсем прав, себестоимость пойдет "на карман", и чем больше мелких шурупов тем весомее карман. |
|||
42
zelenprog
13.06.24
✎
09:28
|
(32) >> Проблема в использовании не правильного инструмента
>> Если надо читать напрямую dbf-ки (и делать к ним запросы) >> Для этого есть множество правильных инструментов А какие есть для этого "правильные" инструменты? Я раньше пользовался прямыми запросами 1С++ (ODBCDatabase, ODBCRecordSet). Но примерно год назад мне почему-то показалось что 1sqlite лучше. И с тех пор использую 1sqlite. |
|||
43
Гена
13.06.24
✎
09:32
|
Я бы в зоопарках выделил бы клетку с табличкой "Перфекционист". "С рук не кормить!" Стол, кресло, комп... и пусть он там сидит и высчитывает какая точность числа π ему нужна.
|
|||
44
Chai Nic
13.06.24
✎
09:33
|
(41) Округление может пройти как в плюс так и в минус. И пустой карман вряд ли понравится хозяину.
|
|||
45
Chai Nic
13.06.24
✎
09:34
|
(43) Дело не в точности вычислений, а в том чтобы не получать потери точности по вине внешних по отношению к 1с сервисов и библиотек..
|
|||
46
Гена
13.06.24
✎
09:40
|
Дети дошкольного и младшего школьного возраста имеют право на любой вид рекреационной деятельности при условии, что данный вид деятельности блокирует активацию слёзных каналов.
|
|||
47
Garykom
13.06.24
✎
09:46
|
(42) 1sqlite работает извращенно
фактически dbf-ки грузятся во ВТ SQLite и запросы к этим ВТ |
|||
48
Djelf
13.06.24
✎
09:55
|
(47) Превосходное экспертное мнение, но нет. Это работает по другому.
|
|||
49
Гена
13.06.24
✎
10:08
|
На первом курсе Физфака МГУ на первой же лабораторной работе отсекают перфекционизм через двойку.
Прибор: обычная плашка, которая может менять угол наклона, закреплённый транспортир и брусок из того же дерева. Десяток-другой опытов определения угла, при котором брусок начинает скользить по плашке. Известно, что коэффициент трения равен тангенсу угла наклона плашки. Так вот, за ответы вроде: 0.562213 +/- 0.039367 сразу выгоняли на повторную сдачу, т.к. правильный ответ: 0.56 +/- 0.04 |
|||
50
Djelf
13.06.24
✎
11:55
|
(49) Я что-то подобное припоминаю, мне прибор измерения электрона выдал что он размером примерно с футбольный мяч.
Я не поверил - перемерял еще несколько раз, да чтоб тебя, все равно таких же размеров. Что делать, что делать... подделать показатели прибора и посчитать все задом наперед. Препод, конечно удивился такой точности измерений, но разброс я заложил туда значительно больше чем у тебя написано, иначе было бы слишком непровдоподобно на таком то старье. Вот так я и упустил шанс стать первооткрывателем электрона футбольного размера.... |
|||
51
АгентБезопасной Нацио
13.06.24
✎
12:03
|
(50) Это был не электрон, а глобус для совы...
|
|||
52
Garykom
13.06.24
✎
12:19
|
(48) Как тогда это работает?
|
|||
53
Garykom
13.06.24
✎
12:25
|
(49) В данном случае есть логика
Ибо погрешность можно округлить, но вопрос почему до сотых только, хотя и логично т.к. в большую А в значении коэффициента кол-во знаков после погрешности не имеет смысла |
|||
54
Гена
13.06.24
✎
12:30
|
(53) Погрешность заложена в точности прибора, в данном случае - риски транспортира.
Это... в 90-х вваливается в лабораторию какой-то кекс с лопатником денег: - Мне нужен прибор для измерения вакуума! - Какой точности? Ну там 10^-2 или даже 10^-5 ? - Абсолютной! - ?! - Чтобы полный вакуум мог показать! - ?! - Ну это когда стрелка манометра на нуле! |
|||
55
Garykom
13.06.24
✎
12:33
|
(54) В данном случае речь о расчетной погрешности
В которой учитывается (прибавляется) погрешность прибора(ов) для измерений Да если их погрешность велика то можно округлить, но не всегда А еще есть математическо-статистические методы снижения погрешности... |
|||
56
Djelf
13.06.24
✎
12:43
|
(52) 1sqlite не имеет прямого доступа к dbf, эта штука работает за движком обработки таблиц средствами 1С, тут Орефков гениально решил проблему попадания в индексы, то что делается на фоксе через "яяяяяя" и т.п. в 1sqlite не требуется.
Например мускул не будет работать с индексом id+code, если id не указан, а 1sqlite будет работать с таким индексом. Так, с индексами вроде разобрались, при чтении учитывается индекс и табличка становится сильно уже... Чтение dbf происходит построчно, было небольшое препинание Орефкова и Hogic`а как читать эту строку, Орефков сделал это быстрее. Это не так все происходит, dbf не грузятся в sqlite напрямую, sqlite ничего про dbf знать не знает. Потребуются уточнения - сделаю. |
|||
57
Garykom
13.06.24
✎
13:00
|
(56) Эмм я ошибся и 1sqlite не грузит все данные из dbf (через движок 1С 7.7) в свои ВТ?
Чтобы затем делать к ним запросы |
|||
58
Garykom
13.06.24
✎
13:01
|
(57)+ Я очень сомневаюсь что внутри реализован транслятор запросов sql в запросы 1С 7.7
Или транслятор запросов sql в работу с таблицами (dbf на файловой) через курсоры и поиск записей в dbf (с опорой на индексы) |
|||
59
Злопчинский
13.06.24
✎
13:02
|
(52) наверняка через микросервис на Go! ;-)
|
|||
60
Djelf
13.06.24
✎
13:03
|
(58) Нет, конечно такого нет, всем рулит sqlite с размером мозгов меньше метра.
|
|||
61
Chai Nic
13.06.24
✎
13:11
|
(50) У ядерных физиков такое бывает, плюс-минус пара порядков иногда считается очень высокой точностью. То есть, утверждение "размер электрона вписывается в футбольный мяч" это очень точно, по сравнению с "он вписывается в десяток световых лет")
|
|||
62
Гена
13.06.24
✎
13:16
|
(61) Не тот случай. Просто у электрона нет размеров. Для судентов есть лаба по определению ЗАРЯДА электрона по фото его следа в магнитной камере в виде скручивающейся спирали. А далее из заряда просто через формулу высчитывается лоренцевский радиус для заряда.
Где коллега потерял 16 порядков? Скорее всего в перепутке констант в СИ и СГС ) |
|||
63
Garykom
13.06.24
✎
13:27
|
(60) Вопрос в том сами данные (из таблиц dbf) во временные таблицы sqlite копируются?
Или происходит онлайн чтение через движок 1С 7.7 в момент выполнения запросов? |
|||
64
Djelf
13.06.24
✎
13:44
|
(61) Ты меня успокоил, я то думал что отбиватся мухобойкой от электронов это не нормально, а оказывается это в пределах точной науки.
|
|||
65
Garykom
13.06.24
✎
14:26
|
Узнавать размер электрона это как узнавать "размер" ветра
Хотя у циклона или смерча/торнадо он вполне есть |
|||
66
orefkov
13.06.24
✎
19:45
|
(24)
Вот это вот фраза "SQLite может хранить данные любого типа — вне зависимости от того, какой тип указан на столбце." - ложь. Вернее, ее следует понимать так - "sqlite при записи данных не смотрит, какой тип данных был объявлен для колонки и записывает туда то, что ему передали, как есть". Сам sqlite умеет работать только с четырьмя видами данных - целые 64битные числа, текст, блоб (ДвоичныеДанные в терминах восьмерки) и double, т.е. число в формате IEEE-754. Когда 1sqlite посредством обращения к функциям движка 1С вычитывает данные, ему надо отдать их потом в sqlite только в одном из этих вариантов. Поэтому любое нецелое число приходится преобразовывать в double, что всегда может приводить к потере точности. Была у меня мысль сделать возможность как-то указать 1sqlite, что такой-то столбец надо отдавать как целое с масштабированием (например, поле 8.3 сначала умножать на 1000 и выдавать в sqlite как целое), но потом я от этого отказался, так как в общем случае sqlite в выражениях всё-равно где-нибудь да применит вещественную арифметику, то есть этот факт всё-равно составителю запроса придётся учитывать. |
|||
67
orefkov
13.06.24
✎
20:01
|
(57)
Слово "Виртуальные" в названии "виртуальные таблицы" говорит о чём-либо? На то они и виртуальные, что в них нет самих данных, а 1sqlite выдаёт данные только по запросу движка sqlite и только те, которые просят. Грубо говоря, движок sqlite парсит запрос, разбивает его выполнение на отдельные элементарные операции с таблицами, и дёргает эти операции, типа "Встань на первую строку из 1sjourn, где iddocdef = стольки-то, а datedoc больше или равно стольки-то и прочитай IDDOC". Выбираем нужный индекс, находим первую строку, читаем IDDOC, отдаем в sqlite. Он дальше запрашивает - "Найди строку в DHXXXX, где IDDOC=стольки-то". И т.д. |
|||
68
orefkov
13.06.24
✎
20:04
|
+(67)
Ну и потом там команды типа "перейди на следующую строку в 1SJOURN с такими же условиями и ответь, есть такая или уже нет". И так в цикле. 1sqlite просто умеет хорошо подбирать индексы по выдаваемым условиям и использовать их при навигации по таблицам 1С. |
|||
69
Djelf
13.06.24
✎
22:09
|
Гуру пришел, отлично!
Объяснения у Орефкова значительно более четкие и более понятные чем мои. Хотя хрен тут объяснишь магию работы sqlite "на пальцах". Ускорение идет за счет того что мы не читаем Объекты, мы читаем сырые данные и как сказано в (67) читаем именно те данные, которые нужны, а не все. На самом деле это не так, сначала читаем то мы все поле dbf с диска, а потом выкусываем нужные данные, но этим моментом можно и пренебречь. Иногда пишу псевдокод на языке 1с для понимания начинающим, навигационный способ выборки вполне подходит для этого. Отдельно хочу отметить тест Ёпрста (сравнение фокса и 1sqlite). Пока Ёпрста победить не удалось, фокс превосходен, хотя в монопольном режиме 1sqlite (или в режиме транзакции) побеждает, но это не то что хочется, попытки сделать хинт в стиле (NOLOCK) привели к сбоям движка 1С, пришлось отказаться, не понятно как это освоить. И... жуть как там Ёпрст в индекс попадает, в 1sqlite это не требуется. Зато новые плюшки в sqlite прекрасны, фокс с такими выкрутасами не справится... |
|||
70
Djelf
13.06.24
✎
22:16
|
Читал как-то Железную Крысу Гаррисона, там хакнули хаб банковских транзакций на предмет слива округления на отдельный счет.
С IEEE-754 запаритесь так зарабатывать, даже в случае хаба. Что-то накапает, но не миллионы... |
|||
71
Garykom
14.06.24
✎
08:42
|
(67) Теперь понял, расширения с виртуальными таблицами
https://habr.com/ru/articles/528882/ Имхо по сабжу выход это в 1sqlite Numeric поля DBF представлять двумя полями виртуальной таблицы Одно TEXT как исходное, второе REAL преобразование через double |
|||
72
zelenprog
14.06.24
✎
09:13
|
(66) >> Сам sqlite умеет работать ... с ... double, т.е. число в формате IEEE-754.
>> Когда 1sqlite посредством обращения к функциям движка 1С вычитывает данные, ему надо отдать их потом в sqlite только в одном из этих вариантов. Поэтому любое нецелое число приходится преобразовывать в double, что всегда может приводить к потере точности. А как выполнятся это преобразование? Какой командой? Наверно используется какая-то функция языка, на котором написан 1sqlite. Верно? Это какая-то функция Си или С++? |
|||
73
zelenprog
14.06.24
✎
09:18
|
(71) >> Имхо по сабжу выход это в 1sqlite Numeric поля DBF представлять двумя полями виртуальной таблицы
>> Одно TEXT как исходное, второе REAL преобразование через double Ты имеешь ввиду, что это надо реализовать "внутри" 1sqlite? Или это я должен в запросе запросить два поля разного типа для одного числового поля 1С? Например:
Что это даст? |
|||
74
Garykom
14.06.24
✎
09:29
|
(73) >Ты имеешь ввиду, что это надо реализовать "внутри" 1sqlite?
Угу >Что это даст? Возможность получить строку "44,66" вместо числа 44.6599999999999 Со строкой можно уже работать разными методами https://stackoverflow.com/questions/14308467/convert-text-to-numbers-in-sqlite Проблема то в (25) |
|||
75
Garykom
14.06.24
✎
09:40
|
(73) Подразумевал примерно так
| ДокРеализацияТЧ.СуммаREAL AS [Сумма_Число :Число.15.2], | ДокРеализацияТЧ.СуммаTEXT AS [Сумма_Текст :Строка], или не оба поля с добавкой (постфиксом) а только одно |
|||
76
orefkov
14.06.24
✎
11:29
|
(72)
Точно не помню, исходники надо смотреть, но это принципиально. Принципиально то, что в double десятичные числа в общем виде невозможно представить точно. Потому что дроби там идут по степеням двойки, а не десятки. |
|||
77
Djelf
14.06.24
✎
13:43
|
Что любопытно, sqlite cli и sqlite expert возвращают 44.66
Если судить по выводу 1.0/3=0.333333333333333 то значение округляется до 15 цифр после запятой Хм, а в 1sqlite округление происходит при 12 знаках SELECT 44.66 [44_66 :Число.30.12] Код sqlite expert закрыт, а вот как это делает sqlite cli по коду не понятно. |
|||
78
Garykom
14.06.24
✎
14:46
|
Интересно если
sqlite3_result_double(pCtx, atof(val)); тупо поменять на sqlite3_result_double(pCtx, atold(val)); для сабжа поведение изменится?
|
|||
79
Djelf
14.06.24
✎
15:12
|
Может и да, но просто так компилятор не получится сменить, а msvc 6.0 не видит atold, хотя вроде в хидерах это определение есть.
|
|||
80
Garykom
14.06.24
✎
15:24
|
(79) math.h или другую версию stdlib.h нельзя легко подключить?
|
|||
81
Djelf
14.06.24
✎
16:24
|
Подключить то можно, но выскочит кучка ошибок. msvc 6.0 слишком устарел.
|
|||
82
Arbuz
14.06.24
✎
17:07
|
![]() |
|||
83
Djelf
15.06.24
✎
12:11
|
(78) Что-то я вчера не внимательно посмотрел, atold должен выдать long double, а нужен просто double. Не сработало бы.
С консолькой sqlite вроде понятно, в нее 44.66 выводится как строка и при этом как-то округляется, до исходного значения. 1sqlite ведет себя аналогично при типизации :Строка. А вот с sqlite expert не понятно, typeof(44.66) выдает тип real, но значение все равно 44.66, видать что-то такое хитрое там замутили, хотя он на DevExpress построен, может так там грид устроен. |
|||
84
Garykom
15.06.24
✎
14:50
|
(83) имхо проблема в atof(val)
надо отладкой глянуть что оно выдает для 44.66 |
|||
85
Djelf
15.06.24
✎
15:29
|
(84) Он не может выдать по другому, я уже пару раз давал ссылку на каркулятор: https://www.h-schmidt.net/FloatConverter/IEEE754.html
Вот что выдает калькулятор, то там и внутри. Ссылку на расширение decimal extension я тоже давал: https://sqlite.org/floatingpoint.html Но оно не умеет делить. Как представить совершенно точно 1/3 или число пи? sqlite пытается стремиться к той точности вычислений IEEE-754 что сейчас реализована. С некоторыми вариациями... В десятичной арифметики им бы поучиться у 1С, у них то всегда все точно ;) Сколько вариантов округления насчитаешь? https://en.wikipedia.org/wiki/Rounding#:~:text=Rounding%20or%20rounding%20off%20means ,expression%20%E2%88%9A2%20with%201.414. В этом то и проблема и решения не существует. В принципе можно выдавать результат как строку, тогда она будет приближено округлена, затем выделять часть до точки и часть после нее, а потом загонять в представление 1С, но это может нарушить совместимость со старыми версиями 1sqlite, что не приемлимо. |
|||
86
orefkov
15.06.24
✎
18:54
|
(84)
atof преобразует строку в double, точнее в sqlite уже некуда. Число 44.66 невозможно представить в double точно. Бинарное представление double 01000010001100101010001111010111 = 44.659999847412109375 Следующее по величине число в double 01000010001100101010001111011111 = 44.660030364990234375 И между этими числами невозможно записать другое. Если есть желание получать число текстом и реализовать более точную арифметику в запросе вручную - ну, пробуйте. Однако на практике я не сталкивался с нерешаемыми проблемами при расчетах в double. |
|||
87
orefkov
15.06.24
✎
19:01
|
(85)
>> В десятичной арифметики им бы поучиться у 1С, у них то всегда все точно В семёрке при делении она считает вроде только до 13 знаков после запятой. А так да, сложение/вычитание/умножение выполняется точно в десятичных знаках. Но вычисления нам кажутся точными только психологически, потому что мы привыкли к десятичной записи и в бухгалтерии используем её же. Но допустим, 10 / 3 точно так же невозможно без потери точности представить в десятичной записи, как 4466 / 100 в двоичной. То есть какую бы систему счисления вы не использовали, от ошибок потери точности не уйти, и их надо уметь учитывать. Любой, кто налоги считал по строкам и по итого, об этом знает. |
|||
88
Garykom
15.06.24
✎
20:18
|
(86) Тогда непонятно почему из double 44.659999847412109375 получается в sqlite 44.6599999999999
Вместо 44.060001373291015625
|
|||
89
orefkov
15.06.24
✎
20:50
|
Потому что 1С при преобразовании из double в CNumeric по дефолту округляет до 13 знаков после запятой.
|
|||
90
orefkov
15.06.24
✎
20:51
|
(88)
У тебя на картинке 44.06, а не 44.66 |
|||
91
Garykom
15.06.24
✎
21:07
|
(90) сорри ошибся да
|
|||
92
Garykom
15.06.24
✎
21:10
|
44.66 = 44.659999847412109375
|
|||
93
Garykom
15.06.24
✎
21:14
|
Тогда в запросах писать вместо
| ДокРеализацияТЧ.СуммаНДС, с округлением | round(ДокРеализацияТЧ.СуммаНДС, 2), и все |
|||
94
orefkov
15.06.24
✎
21:32
|
(93)
Загвоздка в том, что согласно https://sqlite.org/lang_mathfunc.html в sqlite нет функции round. Хотя можно добавить. |
|||
95
Garykom
15.06.24
✎
21:46
|
|
|||
96
orefkov
15.06.24
✎
21:54
|
+(94)
А другая загвоздка в том, что round тоже будет возвращать double, а как мы уже выяснили, в double число 44.66 не представимо в точном виде, как ни округляй. |
|||
97
Garykom
15.06.24
✎
21:57
|
(96) в sqlite есть тип decimal
но только в памяти/вычислениях при хранении в базе только integer или real |
|||
98
orefkov
15.06.24
✎
21:59
|
(95)
То, что вы показываете, это то, что командная оболочка sqlite выводит в результате printf к результату запроса. Да, человеку работающему с микросервисами на Go, сложно объяснять внутреннее устройство бинарного представления чисел в компьютере :) |
|||
99
Garykom
15.06.24
✎
22:12
|
вероятно решить (точнее скрыть-уменьшить) проблему (0) в 1sqlite можно добавив округление в код (78)
до какого знака округлять получать из 1С нечто вроде int powered = pow(10, fi.precession()); double dval = round(atof(val)*powered)/powered; sqlite3_result_double(pCtx, dval); |
|||
100
Garykom
15.06.24
✎
22:21
|
(98) 1. в консоли sqlite функция round() есть
https://sqlite.org/lang_corefunc.html#round 2. тип decimal в sqlite есть, точнее NUMERIC https://sqlite.org/lang_expr.html#castexpr не понял причем тут "внутреннее устройство бинарного представления чисел в компьютере"? |
|||
101
Garykom
15.06.24
✎
22:30
|
(96) да (99) не сработает
|
|||
102
orefkov
15.06.24
✎
22:31
|
(100)
Numeric там фейковый, и он кастит в double. https://sqlite.org/datatype3.html "The expression "CAST(4.0 AS INT)" returns an integer 4, whereas "CAST(4.0 AS NUMERIC)" leaves the value as a floating-point 4.0." Вот оригинальная документация как sqlite работает с числами: https://www.sqlite.org/floatingpoint.html То, что утилита командной строки, которую вы показываете, выдает 44.66 - результат работы не sqlite, а самой утилиты, которая форматирует вывод. То же самое, как в запросе указывать :Число.15.2 Можете проверить sqlite> select decimal(round(44.6599999, 2)); 44.659999999999996589394868351519107818603515625 |
|||
103
Garykom
15.06.24
✎
22:37
|
(102) угу ((
виртуальный какой то numeric введен чисто как заглушка на будущее |
|||
104
Garykom
15.06.24
✎
22:40
|
короче это не баг а фича
или свою арифметику для decimal писать в sqlite или результат при получении из sqlite округлять но тогда арифметика в запросах может не сходиться с 1Сной |
|||
105
Злопчинский
16.06.24
✎
03:24
|
так вот она где копейка... ;-)
насуммируется неточных "44.66" и набежит округлений на копейку в результате...? |
|||
106
MarySue
16.06.24
✎
04:51
|
(104) при получении результата запроса округляйте спокойно до нужной точности и не волнуйтесь за арифметику, всё сойдётся
PS. сабж высосан из пальца, нет там никакой проблемы. |
|||
107
victuan1
17.06.24
✎
14:26
|
(86)
в результат запроса попадает число 44.6599999999999 vs Бинарное представление double 01000010001100101010001111010111 = 44.659999847412109375 Почему количество "девяток" после точки не сходится? Понятно, точность (разрядность) разная |
|||
108
Arbuz
17.06.24
✎
15:52
|
||||
109
Garykom
17.06.24
✎
15:46
|
(106) В коммуналке количество и цены до 9 знаков после запятой
И то точности не хватает, для дробных человеко-месяцев Это когда тарифы установлены годовые, а выставить надо с учетом что человек отсутствовал пару дней в месяце |
|||
110
Arbuz
17.06.24
✎
16:04
|
(109) Меняйте логику, что-то у вас не правильно в консерватории. Мля, цены считаете до 9 знаков после запятой, лютейшая дичь.
|
|||
111
MarySue
17.06.24
✎
22:51
|
(110) +100500
(109) если вы что-то делаете, а оно не получается - попробуйте делать это по-другому если вы пишете нетленку, в которой результат расчётов чувствителен к 9 знакам после запятой (и при этом вы не рассчитываете для Илона Маска траекторию полёта ракеты до Марса) - вы делаете что-то сильно не то. |
|||
112
Garykom
17.06.24
✎
23:36
|
(110) (111) Вы просто не знакомы с предметкой
Я поначалу тоже думал что в консерватории не але и надо по другому Но оказалось законотворцы слегка того И просто особенность отрасли Простой пример всем вероятно знакомый: Какая сумма НДС при сумме товара 1 копейка? |
|||
113
Arbuz
18.06.24
✎
17:37
|
(112) >Какая сумма НДС при сумме товара 1 копейка?
Теперь скажи ещё, что массово выставляете счёта с такими суммами. Если да, ну мало ли, то ндс ноль рублей ноль копеек. Насчёт "не сталкивались с предметкой" — это зря. Был опыт работы в начале десятых с водоснабжающей организацией на 45к абонентов. Не такая большая организация чтобы держать штат профессиональных разработчиком, но и не такая маленькая чтобы не хлебануть учётных проблем в соответствии с нормативным законодательством. Что характерно, была у них 1С внедрённая какими-то там гениями и тоже считать им приходилось с подвыпердами в ночную смену в мыле, при том, что у них была старая программа на фоксе, которая считала всё легко и непринуждённо, но не учитывала какие-то современные требования. И они хотели чтобы 1С считала также и сверху по-мелочи. После некоторого изучения громадного количества требований нормативки и консультаций с ними, всё им было сделано за пару недель с перекурами двумя прогами. Девчонки расчётчицы были счастливы. |
|||
114
trdm
18.06.24
✎
19:31
|
(81) > Подключить то можно, но выскочит кучка ошибок. msvc 6.0 слишком устарел.
Надо переходить на 7.8 :) А то настафигели эти ограничения. |
|||
115
Djelf
19.06.24
✎
07:40
|
(114) А и не парюсь особо сильно, это был частный случай, и он бы и не сработал. В тяжелых случаях (curl1c/карлик) использую микс библиотек с разными версиями icl, они линкуются с vc.6 без проблем.
Проблема в том что я не "вкурил" шаблоны, пытался, пытался, но бестолку. Шаблон ВК Орефкова (он мне очень нравится) мне не получается перевести на что-то более современное, даже ваш хваленый GPT не помогает, вернее выдает такой бред... Поэтому немного и привязан к vc.6 |
|||
116
zelenprog
20.06.24
✎
10:43
|
(83) В итоге получается, что оптимальное решение - это сделать типизацию сразу в запросе:
Можно объяснить: какая последовательность действий при этом выполняется? Что (какой движок) будет выполнять эту типизацию-округление? Выше в обсуждениях есть вот такое описанием работы 1sqlite: (56) >> 1sqlite не имеет прямого доступа к dbf >> sqlite ничего про dbf знать не знает >> эта штука работает за движком обработки таблиц средствами 1С (66) >> ... 1sqlite посредством обращения к функциям движка 1С вычитывает данные >> ... надо отдать их потом в sqlite >> ... нецелое число приходится преобразовывать в double, что всегда может приводить к потере точности (67) >> ... 1sqlite выдаёт данные только по запросу движка sqlite и только те, которые просят >> ... движок sqlite парсит запрос, разбивает его выполнение на отдельные элементарные операции с таблицами, и дёргает эти операции Значит, последовательность действий следующая: 1) Какой-то метод в модуле 1С вызывает выполнение запроса через 1SQLite. 2) 1SQLite получает текст запроса, передает этот запрос в SQLite. 3) SQLite анализирует текст запроса и запрашивает данные у 1SQLite. 4) 1SQLite для получаения данных вызывает функции движка 1С. 5) Движок 1С читает реквизит из dbf как строку "44,66" (так как выше было сказано, что numeric в 1С-dbf хранится как строка). 6) Затем движок 1С видит, что формат этого dbf-реквизита - это numeric(15,2), и преобразует строку "44,66" в "свой" числовой формат 1С 44,66. Здесь, как я понимаю, пока искажений числа нет. Данные (включая число 44,66) возвращаются из 1С в 1SQLite. 7) 1SQLite "принимает" данные (число 44,66) от движка 1С и преобразует их в формат SQLite для передачи в SQLite. Так как SQLite понимает только double, то 1SQLite преобразует число 44,66 в 44,6599999. И передает это число в SQLite. 8) SQLite обрабатывает полученные данные (то есть работает с числом double 44,6599999). 9) Результат выполнения всего запроса передается из SQLite в 1SQLite. 10) Теперь 1SQLite должна вернуть данные в вызывающий метод 1С. Значит, здесь должно быть еще одно преобразование данных результата запроса из формата SQLite в формат 1С. 11) 1SQLite видит, что в тексте запроса для реквизита указан тип с точностью (:Число.15.2), и поэтому округляет double 44,6599999 (полученный от SQLite) до формата 1С 44,66. 12) 1SQLite заполняет ИндексированнуюТаблицу результатами запроса, преобразованными в формат 1С. А обработка текста запроса в целом выполняется следующим образом: 1) Типизацию в запросе "видит" только 1SQLite. 2) Перед передачей текста запроса в SQLite, 1SQLite преобразует этот запрос (убирает из запроса типизацию). 3) 1SQLite передает преобразованный текст запроса (без типизации) в SQLite. 4) Конечное округление и типизацию результатов запроса делает именно 1SQLite. Все верно? Правильно ли я понимаю всю цепочку преобразований, по которой проходит это "несчастное" число 44,66? Если что-то неправильно - поправьте. |
|||
117
Arbuz
20.06.24
✎
15:54
|
|
|||
118
Волшебник
20.06.24
✎
17:20
|
(117) ибо нефиг
|
|||
119
zelenprog
21.06.24
✎
09:48
|
(117) >> Из неправильного...
А ты знаешь как правильно? (117) >> ...форточку открой уже. У нас же не тюрьма, мы тебя силой не держим :) "Ты свободен словно птица в небесах" - иди на природу, дыши свежим воздухом. |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |