Такая кодировка не позволяет делать zerocopy парсинг. Плюс, она не позволяет брать подстроки (что часто используется в дедупликации).
Вообще кодировка скорее похожа на доменноспецифичный алгоритм сжатия, чем на собственно кодировку. Но в таком случае, кажется, можно сделать сильно эффективнее(быстрее, компактнее), если подходить к этому именно как к сжатия (и можно жать не байты, а напрямую кодпоинты).
P.S. кажется Вы это подразумевали, но было бы хорошо добавить в статью чёткое указание, что это кодировка для передачи, а не для использования в оперативной памяти.
Не совсем. Это чисто вычислительный трюк, чтобы работать с бесконечномалыми на компьютере.
Например, через обычные дуальние числа можно автомагически считать точные производные функций.
Гипердуальные числа есть просто обобщение на другие степени. Например, можете рассматривать числа вида:
Где d — означает бесконечномалую, а A — плюс бесконечность. Дальше осталось ввести только таблицу умножения, например:
Тогда в определённых пределах такие числа будут давать корректные результаты. А если заменитьна NaN/исключение, то она даже будет предсказуемой.
Подразумевается, разумеется, что данные числа определены с точностью до. Можно также хранить признак, является ли число "точным", или же там есть o-малое. Тогда можно будет отличать ситуации деления на 0.
Tl;Dr; Если не замахиваться на универсальность, можно достичь хороших результатов с гораздо более простым представлением чисел (и для него будет проще определить границы применимости)
Так я их и не вычитаю. Вопрос в том, что у Вас не хранится информация о коэффициенте при. Поэтому при вычитании разных чисел может получится 0 — это и называется "катастрофическое сокращение" (не в смысле IEEE 754, а в обобщённом смысле, если хотите).
Перечитайте ещё раз чему равна дробь. У Вас получается, что 1/1 = 0. Там член прогрессии пропустили
Так корень же может быть как снаружи так и внутри. Например, чему равно:
Я Вам могу ещё долго таких "каверзных" примеров накидывать.
В целом, Ваша затея не нова. Есть ряд алгебраических подходов к пределам, но все они страдают от фундаментальных ограничений. В зависимости от Ваших ответов Ваш подход может сойтись, например, к гипердуальным числам или к символьным вычислениям.
Ввиду своих ограничений данные подходы тяжело сделать общеприменимыми. Поэтому живут либо на флоатах, либо вообще избегают вещественных чисел.
Те, кому эти все ухишрения действительно нужны, просто строят собственное алгебраическое приближение, которое подходит под конкретную задачу. Общих решений тут, увы, нет.
Вы говорите, что у Вас обычный 0 представляется как 1/A. Но тогда возникает вопрос: чему равно 1 - 1, или A - A. Предположу, что A^-1 и A^0 соответственно.
Но тогда:
Что очевидно глупость. И я уже не говорю про...
Второй вопрос, по Вашей реализации уже. Вы, зачем-то, отсекаете нижние уровни — этого делать нельзя. Типовой пример:
Я ожидаю увидеть в ответе, но Ваша библиотека выдаст что-то другое.
Ну и в целом, в любой адекватной числовой системенеобратим — иначе мы потеряем дистрибутивность/ассоциативность. А без дистрибутивности/ассоциативность никакие числа не нужны... (Ну ладно флоаты как-то живут — но разве это жизнь)
А компилятору и не нужно это определять. И тем более компилятор не интересуют утеряны ли там данные — это уже про логические ошибки, поэтому даже рассматривать их не буду.
Из того что я читаю, Вы можете определить какая часть буфера уже была чем-то заполнена. В таком случае, Вы можете хранить буфер в виде &[MaybeUninit<AtomicU8>] (или другой тип атомиков и пр). Далее, после вычисления какая часть реально заполнена, Вы можете получить эту часть в виде &[AtomicU8]. С ней уже можно либо либо куда-то скопировать, либо выдать какую-нибудь обёртку для прямой работы с буфером.
Конкретно с чтением неинициализированных данных здесь проблем никаких нет(и никакое UB не нужно). И это вполне себе несложно прячется за безопасный интерфейс.
Здесь есть проблема с конкурентным доступом, но о ней Вы ничего пока не говорили. Суть в том, что нужно аккуратно сообщить компилятору, что гонка данных здесь разрешена и прочее. С этим чуть сложнее.
Не, не понимаю. Если Вы хотите работать с данными, которые могут быть неинициализированны, то их нужно хранить в MaybeUninit. В местах использования вызывать assume_init{_ref}, когда как-то удостоверились, что данные фактически были инициализированны.
Делать как в комментарии выше строго нельзя — собственно MaybeUninit был добавлен как раз по этому (раньше был std::mem::uninitialized).
Если Вам кажется, что я упускаю какой-то юзкейс, то приведите пожалуйста набросок кода, который, как Вам кажется, не может быть реализован эффективно без mem::uninitialized.
У него есть гарантии на repr: MaybeUninit<T> всегда будет иметь точно такой же репр, что и T. Поэтому его и указатели/ссылки на него вполне себе можно передавать в extern "C".
Но я честно говоря затрудняюсь понять о каких именно случаях Вы говорите, поэтому если приведёте пример с проблемой — будет проще
Такая кодировка не позволяет делать zerocopy парсинг. Плюс, она не позволяет брать подстроки (что часто используется в дедупликации).
Вообще кодировка скорее похожа на доменноспецифичный алгоритм сжатия, чем на собственно кодировку. Но в таком случае, кажется, можно сделать сильно эффективнее(быстрее, компактнее), если подходить к этому именно как к сжатия (и можно жать не байты, а напрямую кодпоинты).
P.S. кажется Вы это подразумевали, но было бы хорошо добавить в статью чёткое указание, что это кодировка для передачи, а не для использования в оперативной памяти.
Не очень, т.к. очень много зависимостей по данным (из-за переключения режимов)
Не совсем. Это чисто вычислительный трюк, чтобы работать с бесконечномалыми на компьютере.
Например, через обычные дуальние числа можно автомагически считать точные производные функций.
Гипердуальные числа есть просто обобщение на другие степени. Например, можете рассматривать числа вида:
Где d — означает бесконечномалую, а A — плюс бесконечность. Дальше осталось ввести только таблицу умножения, например:
Тогда в определённых пределах такие числа будут давать корректные результаты. А если заменить
на NaN/исключение, то она даже будет предсказуемой.
Подразумевается, разумеется, что данные числа определены с точностью до
. Можно также хранить признак, является ли число "точным", или же там есть o-малое. Тогда можно будет отличать ситуации деления на 0.
Tl;Dr; Если не замахиваться на универсальность, можно достичь хороших результатов с гораздо более простым представлением чисел (и для него будет проще определить границы применимости)
А, действительно. Это я перепутал. Ну тогда в выкладках выше нужно немного поправить вычитаемый многочлен.
P.S. обычно определяют не "бесконечность", а "бесконечномалую" — впрочем это двойственные понятия.
Ну, можно потом результат разделить на
и уже будет не 0. В работе с бесконечностями всегда можно прощупать скольугодно малые колебания.
А, ну тогда Вы придумали (гипер)дуальные числа.
Впрочем идея может и красивая, но перестраивать граф, скорее всего, гораздо эффективнее. Хотя зависит от деталей задачи.
Так я их и не вычитаю. Вопрос в том, что у Вас не хранится информация о коэффициенте при
. Поэтому при вычитании разных чисел может получится 0 — это и называется "катастрофическое сокращение" (не в смысле IEEE 754, а в обобщённом смысле, если хотите).
Перечитайте ещё раз чему равна дробь. У Вас получается, что 1/1 = 0. Там член прогрессии пропустили
Вернуть NaN всегда можно, да. Но тогда зачем всё это? Чем обычные флоаты не угодили :)
Попробуйте сформулировать задачу, которую Ваша библиотека пытается решить. Я пока не очень понимаю её скоуп/применимость.
Если мы из
Вычтем
То хотелось бы получить
А не 0.
Забыли +1.
Такой результат начнёт врать если к этому выражению прибавить полином 3-го порядка — так называемое "катастрофическое сокращение"
Так корень же может быть как снаружи так и внутри. Например, чему равно:
Я Вам могу ещё долго таких "каверзных" примеров накидывать.
В целом, Ваша затея не нова. Есть ряд алгебраических подходов к пределам, но все они страдают от фундаментальных ограничений. В зависимости от Ваших ответов Ваш подход может сойтись, например, к гипердуальным числам или к символьным вычислениям.
Ввиду своих ограничений данные подходы тяжело сделать общеприменимыми. Поэтому живут либо на флоатах, либо вообще избегают вещественных чисел.
Те, кому эти все ухишрения действительно нужны, просто строят собственное алгебраическое приближение, которое подходит под конкретную задачу. Общих решений тут, увы, нет.
Так на практике и флоаты работают. Зачем тогда подход автора?/s
Так не выйдет. Например для любого
верно
Где
какая-то величина меньшая по порядку чем
.
То есть, если
, то достаточно взять
и "нормальная" логика сломается.
Ну и Вы не ответили на вопрос с корнем. Ну или даже ещё проще: чему равно
?
Чему равен
?
Edit: или даже проще:
?
Как уже сказал, это нельзя исправить
Подход типичный, но он не работает.
Вы говорите, что у Вас обычный 0 представляется как 1/A. Но тогда возникает вопрос: чему равно 1 - 1, или A - A. Предположу, что A^-1 и A^0 соответственно.
Но тогда:
Что очевидно глупость. И я уже не говорю про
...
Второй вопрос, по Вашей реализации уже. Вы, зачем-то, отсекаете нижние уровни — этого делать нельзя. Типовой пример:
Я ожидаю увидеть в ответе
, но Ваша библиотека выдаст что-то другое.
Ну и в целом, в любой адекватной числовой системе
необратим — иначе мы потеряем дистрибутивность/ассоциативность. А без дистрибутивности/ассоциативность никакие числа не нужны... (Ну ладно флоаты как-то живут — но разве это жизнь)
А компилятору и не нужно это определять. И тем более компилятор не интересуют утеряны ли там данные — это уже про логические ошибки, поэтому даже рассматривать их не буду.
Из того что я читаю, Вы можете определить какая часть буфера уже была чем-то заполнена. В таком случае, Вы можете хранить буфер в виде &[MaybeUninit<AtomicU8>] (или другой тип атомиков и пр). Далее, после вычисления какая часть реально заполнена, Вы можете получить эту часть в виде &[AtomicU8]. С ней уже можно либо либо куда-то скопировать, либо выдать какую-нибудь обёртку для прямой работы с буфером.
Конкретно с чтением неинициализированных данных здесь проблем никаких нет(и никакое UB не нужно). И это вполне себе несложно прячется за безопасный интерфейс.
Здесь есть проблема с конкурентным доступом, но о ней Вы ничего пока не говорили. Суть в том, что нужно аккуратно сообщить компилятору, что гонка данных здесь разрешена и прочее. С этим чуть сложнее.
Не, не понимаю. Если Вы хотите работать с данными, которые могут быть неинициализированны, то их нужно хранить в MaybeUninit. В местах использования вызывать assume_init{_ref}, когда как-то удостоверились, что данные фактически были инициализированны.
Делать как в комментарии выше строго нельзя — собственно MaybeUninit был добавлен как раз по этому (раньше был std::mem::uninitialized).
Если Вам кажется, что я упускаю какой-то юзкейс, то приведите пожалуйста набросок кода, который, как Вам кажется, не может быть реализован эффективно без mem::uninitialized.
P.S. Или Вы буквально оставляете UB в коде?
Тогда о чём речь?
У него есть гарантии на repr: MaybeUninit<T> всегда будет иметь точно такой же репр, что и T. Поэтому его и указатели/ссылки на него вполне себе можно передавать в extern "C".
Но я честно говоря затрудняюсь понять о каких именно случаях Вы говорите, поэтому если приведёте пример с проблемой — будет проще