LINUX.ORG.RU

Numerical tower в Racket и прочих Scheme

 numerical tower, ,


0

4

Всем привет!
Продолжаю пилить свой Scheme на Java, сейчас реализую numerical tower. Осталось сделать только complex numbers.

1)
Но тут возник вопрос: как надо правильно реализовывать numerical tower?
Проблема в том, что в Java numerical tower нет, да и вообще, числа реализованы довольно кривовато - много special cases, ограничений и костылей.
Мне бы хотелось использовать встроенные в джаву int, long, float, double и др. числовые типы + добавить свои. Только вот как по-хорошему скрестить ежа с ужом?

Одна из проблем: не очень понятно как красиво и просто конвертировать из одного типа в другой (автоматически). Например, чтобы дефолту всегда использовались int, если их не хватает, то автоматически переходить на long, потом BigInteger и тд.

Есть какие-нибудь стандартные подходы к данной проблеме?

Нашел вот такую штуку:
http://www.ccs.neu.edu/racket/pubs/padl12-stff.pdf

2)
Не понимаю один пример в Racket:

> #e2.3
23/10

> (inexact->exact 2.3)
2589569785738035/1125899906842624

В Guile оба возвращают одно и то же, что ИМХО логично.

В Racket просто решили сделать такую 'оптимизацию', чтобы лишних вычислений не делать?

★★★★★

Последнее исправление: kovrik (всего исправлений: 1)

не очень понятно как красиво и просто конвертировать из одного типа в другой (автоматически)

https://github.com/racket/racket/blob/master/racket/src/racket/src/numarith.c...

пример в Racket

В Racket inexact->exact возвращает ровно то, что передано в значении. 2.3 по-умолчанию хранится в double. 2.3 ровно в double непредставимо.

В Guile скорее всего inexact->exact делает что-то вроде:

> (rationalize (inexact->exact 2.3) 1/10000)
23/10

как красиво и просто конвертировать из одного типа в другой (автоматически)

Технически, если опустить оптимизации, то вся арифметика в выглядит примерно как:

(defun (+ x y)
  (if (+-in-

monk ★★★★★
()
Ответ на: комментарий от monk

Какой вариант более правильный - Racket'овский или Guile'вский?
Я сделал как в Guile.

Большое спасибо за ссылку, поизучаю исходники Racket'а.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Какой вариант более правильный - Racket'овский или Guile'вский?

Формально — Racket'овский. Он даёт точное значение соответствующее inexact числу. И Guile'вский из Racket'овского можно получить, а наоборот — нет.

Но для практического использования Guile'вский удобнее.

monk ★★★★★
()

В Guile оба возвращают одно и то же, что ИМХО логично.

У меня не возвращают

GNU Guile 2.0.13

scheme@(guile-user)> (define a 2.3)
scheme@(guile-user)> a
$1 = 2.3
scheme@(guile-user)> (inexact->exact a)
$2 = 2589569785738035/1125899906842624
scheme@(guile-user)> (inexact->exact 2.3)
$3 = 2589569785738035/1125899906842624
scheme@(guile-user)> #e2.3
$4 = 23/10

monk ★★★★★
()
Ответ на: комментарий от monk

Да, ты прав, на домашнем ноуте более свежая версия Guile возвращает такое же значение, как и Racket.

Еще вопрос: правильно ли я понимаю, что все реализации стараются хранить числа в самом легком типе (ради памяти и производительности), а если его не хватает, то делают upcast до следующего типа?
Делаются ли когда-нибудь downcast'ы обратно?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

то делают upcast до следующего типа

В общем да. Но upcast может случиться только в результате арифметической операции.

Делаются ли когда-нибудь downcast'ы обратно?

Естественно.

> (define a 10000000000000000000)
> (fixnum? a)
#f
> (define b 9999999999999999955)
> (fixnum? b)
#f
> (fixnum? (- a b))
#t

Упрощённо выглядит так: вычисляется с точностью, достаточной для хранения результата (как правило, без оптимизаций, это тип на один выше типов аргументов). Потом результат приводится к наименьшему возможному типу.

monk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.