Это интересный вопрос, поскольку он затрагивает несколько тонких вещей.
% expr int(0.57 * 10000)
5699
Этот код (без подстановок, поэтому он работает «неудивительно») показывает, что числа с плавающей запятой сами по себе удивительны. В частности, 0.57 не имеет точного представления в качестве числа с плавающей запятой IEEE с двойной точностью (то есть base-2); на самом деле его представление немного ниже, чем точно 0,57, и поэтому при округлении в меньшую сторону (что и делает int(...)
; точное значение - 10000) вы переходите к 5699. Такое же поведение вы увидите и с другими языками. .
% expr int([expr 0.57 * 10000])
5700
Теперь это особенно интересно. Сначала вы видите, как выполняются внутренние вычисления, и результирующее число с плавающей запятой преобразуется в строку (потому что другого способа сделать это не существует). Теперь, вы должны использовать Tcl 8.4 (или раньше), где правила рендеринга номеров по умолчанию были (в действительности) тем, что вы получите, напечатав первые 15 значащих цифр номера; в этом случае это дает вам 5700.00000000000
(ну, с некоторым усечением нулей справа), а затем интерпретируется с нуля как первое двойное (ровно 5700.0), а затем преобразуется в 5700.
Правила преобразования чисел изменены в Tcl 8.5. В настоящее время, когда Tcl преобразует числа с плавающей запятой в строки, он генерирует самую короткую строку, которая преобразует обратно в одно и то же число с плавающей запятой (т. Е. Боковое перемещение по земле строк даст одинаковый битовый шаблон в полученном двойном значении). Это, в свою очередь, означает, что теперь вы никогда не увидите различий между двумя вещами выше (Если вы действительно хотите ввести фиксированное количество десятичных знаков, используйте format %.15g [expr 0.57 * 10000]
.)
Вы также не заметите наблюдаемое поведение с 8.0 до 8.4, если правильно подготовите свои выражения, как сообщество рекомендует людям делать это уже более десяти лет:
$ tclsh8.4
% expr {int([expr 0.57 * 10000])}
5699
Это потому, что это не заставляет интерпретировать результат внутреннего вызова expr
как строку. (Это также быстрее и безопаснее.)