Мне кажется, что:
- нативный тип с плавающей точкой (тип, который вы получите, прочитав
1.0
) вашей реализации - IEEE double float; - the
display
вашей Схемы не печатает такие поплавки «правильно» (см. Ниже, я не уверен, что это означает, что он глючит); - ваш
number->string
делает правильные вещи.
Под словом «правильно» я имею в виду «таким образом, чтобы при чтении напечатанного display
получалось эквивалентное число». Я совсем не уверен, что display
требуется , чтобы быть корректным в этом ограничительном смысле, поэтому я не уверен, является ли это ошибкой. Кто-то, кто понимает стандарты Схемы лучше, чем я, может прокомментировать это.
В частности, если родной тип с плавающей точкой языка является двойным с плавающей точкой IEEE, то, например:
(= (* 0.05 3) 0.15)
является ложным, как и
(= (* 0.05 146) 7.3)
Вот пример, который у вас есть в первой строке вашего вывода.
Так что вы, конечно, не должны предполагать, что ваша программа когда-либо выдаст число, равное числу, которое вы получите, прочитав, например, 7.3
, потому что оно не будет.
В приведенном выше описании я тщательно избегал распечатывать числа, и это потому, что я не уверен display
надежен в этом, и, в частности, я не уверен, что ваш display
надежен или что он необходим.
Хорошо, у меня есть реализация на Лиспе, которая это надежно об этом. В этой системе по умолчанию используется формат с плавающей запятой IEEE с одинарной точностью, и я могу заставить читателя читать двойные с плавающей точкой, например, 1.0d0
. Итак, в этой реализации вы можете увидеть результаты:
> (* 0.05d0 3)
0.15000000000000002D0
> (* 0.05d0 146)
7.300000000000001D0
И вы увидите, что это именно то (с точностью до индикатора двойной точности), что number->string
дает вам, а нет что display
дает вам.
Если вы хотите получить представление числа таким образом, чтобы при чтении оно получило эквивалентное число, тогда number->string
- это то, что ты должен доверять В частности, R5RS говорит в разделе 6.2.6, что:
(let ((number number)
(radix radix))
(eqv? number
(string->number (number->string number
radix)
radix)))
- истина, и «это ошибка, если ни один из возможных результатов не делает это выражение истинным».
Вы можете проверить поведение number->float
& float->number
в диапазоне чисел, например (это может предполагать более новую или полную схему, чем у вас):
(define (verify-float-conversion base times)
(define (good? f)
(eqv? (string->number (number->string f)) f))
(let loop ([i 0]
[bads '()])
(let ([c (* base i)])
(if (>= i times)
(values (null? bads) (reverse bads))
(loop (+ i 1) (if (good? c) bads (cons c bads)))))))
Тогда вам следует get
> (verify-float-conversion 0.05 10000)
#t
()
В более широком смысле использование чисел с плавающей запятой, еще больше операций с плавающей запятой, которые являются результатом более сложных вычислений, чем чтение их из какого-либо входного источника, поскольку уникальные индексы в любой табличной структуре чреваты опасностью для ее размещения. довольно мягко: ошибки с плавающей точкой означают, что просто очень опасно предполагать, что (= a b)
верно для чисел с плавающей запятой, даже если математически это должно быть.
Если вы хотите, чтобы такие индексы делали точную арифметику c, и конвертируйте результаты этой арифметики c в числа с плавающей точкой в той точке, в которой вам необходимо выполнить вычисления. Я считаю (но не уверен), что реализации Scheme в настоящее время требуются для поддержки точной рациональной арифметики c (конечно, это, похоже, верно для R6RS), поэтому, если вы хотите считать 20-е (скажем), вы можете сделать это, считая в единицы 1/20
, что является точным, и затем строит числа с плавающей точкой, когда они вам нужны.
Вероятно, безопаснее сравнивать числа с плавающей точкой в том случае, если вы, например, сравниваете число с плавающей точкой, полученное при использовании некоторого начального числа с плавающей точкой значение и умножение его на целое число машины и сравнение его с какой-то более ранней версией, которую вы прочитали string->number
. Но если ваши вычисления более сложны, чем это, вам нужно быть очень осторожным.