Имя: Пароль:
1C
1C 7.7
v7: 1SQlite возвращает число 44.6599999999999 вместо 44.66
0 zelenprog
 
09.06.24
18:10
Здравствуйте!

Есть вот такой запрос по табличной части документа:

    лТекстЗапроса = "
    |SELECT
    |      ДокРеализацияТЧ.IDDOC AS [ID],
    |      ДокРеализацияТЧ.IDDOC AS [Док $Документ.Реализация],
    |      ДокЖурнал.DATE AS [ДатаДок $Дата],
    |      ДокЖурнал.DOCNO AS [НомерДок $Строка],
    |      CASE WHEN ДокЖурнал.ISMARK = '*' THEN 1 ELSE 0 END AS [ПометкаУдаления $Число.1.0],
    |      ДокЖурнал.Closed & 1 AS [Проведен $Число.1.0],
    |
    |      ДокРеализацияТЧ.Номенклатура AS [Номенклатура $Справочник.Номенклатура],
    |      ДокРеализацияТЧ.Количество,
    |      ДокРеализацияТЧ.Единица AS [Единица $Справочник.Единицы],
    |      ДокРеализацияТЧ.Коэффициент,
    |      ДокРеализацияТЧ.Цена,
    |      ДокРеализацияТЧ.Сумма,
    |      ДокРеализацияТЧ.СтавкаНДС AS [СтавкаНДС $Перечисление.СтавкиНДС],
    |      ДокРеализацияТЧ.СуммаНДС,
    |      ДокРеализацияТЧ.Партия AS [Партия $Справочник.Партии],
    |      ДокРеализацияТЧ.Кор,
    |      ДокРеализацияТЧ.ПроцентСкидка,
    |      ДокРеализацияТЧ.ПричинаВозв AS [ПричинаВозв $Справочник.ПричиныВозврата],
    |      ДокРеализацияТЧ.КоличВозв,
    |      ДокРеализацияТЧ.ДатаВыработки AS [ДатаВыработки $Дата],
    |      ДокРеализацияТЧ.НомерПартии,
    |      ДокРеализацияТЧ.ДатаГодности AS [ДатаГодности $Дата],
    |      ДокРеализацияТЧ.КоличествоМарки
    |FROM
    |      ДокументСтроки_Реализация as ДокРеализацияТЧ
    |INNER JOIN
    |      Журнал as ДокЖурнал ON
    |            ДокЖурнал.iddocdef = :ВидДокумента.Реализация AND
    |            ДокЖурнал.IDDoc = ДокРеализацияТЧ.IDDoc
    |WHERE (...)
    |";


В некоторых строках числовые значения (Цена, Сумма, СуммаНДС) возвращаются с длинными десятичными дробями.
Например, вместо суммы НДС 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С?
Например:

    лТекстЗапроса = "
    |SELECT
    |...
    |      ДокРеализацияТЧ.Сумма AS [Сумма_Число :Число.15.2],
    |      ДокРеализацияТЧ.Сумма AS [Сумма_Текст :Строка],
    |...
    |";


Что это даст?
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
(106) Глоток свежего воздуха в этой духотище.
Уважаемый orefkov разжёвывал и разжёвывал, Djelf даже ссылку на теорию округления дал в (85). Но нет — некоторые не могут, душнят и душнят.
ЗЫ: это вы ещё не затронули проблему точной записи трансцендентных чисел, ребята, в любой системе исчисления.
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) В итоге получается, что оптимальное решение - это сделать типизацию сразу в запросе:

    лТекстЗапроса = "
    |SELECT
    |      ДокРеализацияТЧ.Сумма AS [Сумма_Число :Число.15.2],
    |";


Можно объяснить: какая последовательность действий при этом выполняется?
Что (какой движок) будет выполнять эту типизацию-округление?

Выше в обсуждениях есть вот такое описанием работы 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) >> ...форточку открой уже.

У нас же не тюрьма, мы тебя силой не держим :)
"Ты свободен словно птица в небесах" -
иди на природу, дыши свежим воздухом.
Основная теорема систематики: Новые системы плодят новые проблемы.