странная арифметика со swi-прологом

Я нахожу результат очень странным. Почему не 0,3? Может кто-нибудь сказать мне, почему такой результат? Можно ли это исправить.

 ?- X is 5.3-5.
    X = 0.2999999999999998.

    ?- 

Мой второй вопрос: как мне преобразовать «часовую» нотацию «13 .45» ----> «15.30» в числа часов? Например, рассчитанный выше период 15.30-13.45 будет равен 1.85. Но мне нужно оперировать частью часа, а не остатком чисел. Например, 15 1/2 - 13/4, этот способ лучше. я пытаюсь

?- X is (5.3-5)*100/60.
X = 0.4999999999999997.

?- X is (5.3-5)*100//60.
ERROR: ///2: Type error: `integer' expected, found `29.999999999999982'

Какие-либо предложения?


person vincent    schedule 25.02.2014    source источник
comment
Нечто очень похожее недавно появилось в рассылке SWI-Prolog list, где проблема оказалась в том, что Паскаль использует множество магических средств компилятора, чтобы скрыть сложность чисел с плавающей запятой, вызывая путаницу, когда Prolog работает иначе.   -  person Daniel Lyons    schedule 25.02.2014


Ответы (3)


Ответ, который вы получите от SWI, правильный.
Во многих языках программирования и практически во всех системах Prolog реализации с плавающей запятой основаны на двоичной системе (radix = 2). Число 5.3 не может быть представлено точно в этой системе, поэтому выбрано некоторое приближение. Вычитание особенно хорошо подходит для выявления таких неточностей.

?- X is 5.3-5-0.3.
X = -1.6653345369377348e-16.

Пока вы используете числа, которые являются суммой степеней двойки (включая 2 -1, 2 -2, ...), вы получите точные результаты, как насколько это позволяет точность поплавков:

?- X is 5.000244140625-5-0.000244140625.
X = 0.0.

Что должны делать ISO-совместимые системы Prolog должны - это гарантировать, что при записи числа с плавающей запятой с опцией записи quoted(true) число с плавающей запятой записывается таким образом, чтобы его можно было точно прочитать.

Что касается вашего второго вопроса: (//)/2 определяется только для целых чисел. Если вы хотите преобразовать число с плавающей запятой в целое число, у вас есть в ISO Prolog обычные функции LIA-1:

floor/1, truncate/1, round/1, ceiling/1.

?- X is round(5.3).
X = 5.

Однако я бы предпочел использовать (div)/2 вместо (//)/2. Значение (//)/2 больше не поддерживается LIA-1: 2012 (стандарт ISO / IEC 10967-1: 2012) по уважительной причине. См. это и этот ответ для подробностей.

person false    schedule 25.02.2014

По поводу этой строки:

X is (5.3-5)*100//60.

Предикат // - это целочисленное деление, и вы работаете с числами с плавающей запятой. Вы можете сделать это:

X is round((5.3-5)*100/60).

Or

X is round((5.3-5)*100)//60.

Конечно, в обоих этих случаях вы получите 0, потому что это примерно 0.3, если бы вы выполняли операции с плавающей запятой. Так что неясно, действительно ли вы это хотели.

Другие интересные варианты:

?- X is (5.3-5)*100/ 60.
X = 0.4999999999999997.

?- X is float(round((5.3-5)*100) rdiv 60).
X = 0.5.
person lurker    schedule 25.02.2014
comment
Спасибо, я не знала о «раунде». Очень кратко и полезно. - person vincent; 25.02.2014
comment
@vincent, возможно, вы захотите просмотреть арифметические функции SWI Prolog: swi-prolog.org/ pldoc / man? section = functions - person lurker; 25.02.2014

Это связано с арифметикой с плавающей запятой, которая (почти *) всегда несовершенна. Компьютеры работают в двоичном формате, но люди в основном работают с базой 10. Это вносит некоторую неточность здесь и там; насколько велика неточность, зависит от того, как работает рассматриваемое оборудование и (иногда) программное обеспечение. Но главное в том, что вы не можете точно предсказать, какие будут ошибки, только то, что они будут.

В результате нельзя ожидать, что 5.3-5 будет точно 0.3; вам нужно вместо этого проверить, действительно ли он близок (например, в пределах 0,00000000000001). Когда вы получаете результаты, как во втором примере, вам необходимо явно преобразовать в целое число, прежде чем использовать результат. Вы можете узнать больше в этом ответе.

[сноска:] В комментариях ниже я добавлю эту арифметику, используя числа с плавающей запятой, которые являются степенью двойки (то есть 2 ^ i) или суммой степеней двойки (например, 7 = 4 + 2 + 1) можно точно рассчитать на компьютере. Существует философский аргумент относительно того, действительно ли это «арифметика с плавающей запятой». Но это объясняет, почему, например, большинство языков сообщают, что 2.0 + 1.0 равно 3, а 0.2 + 0.1 - это что-то вроде 0.30000000000000004.

person elixenide    schedule 25.02.2014
comment
что всегда несовершенно, неверно. Он идеально подходит для чисел на основе системы счисления 2 в определенном диапазоне. - person false; 25.02.2014
comment
Математика с использованием чисел на основе системы счисления 2 не является арифметикой с плавающей запятой. Это разные концепции. И хотя компьютер может выдавать правильный результат в некоторых сценариях даже для арифметики с плавающей запятой, это не означает, что в вычислениях нет неточностей; это означает, что неточность была слишком незначительной, чтобы уместить ее в области памяти, выделенной для результата вычисления. - person elixenide; 25.02.2014
comment
@EdCottell: Некоторые числа представлены точно, нет никакой неточности для этих чисел. Пожалуйста, обратитесь в LIA-1, если вы хотите узнать подробности. - person false; 25.02.2014
comment
@false Некоторые числа = / = арифметика с плавающей запятой. Вы полностью упускаете суть моего ответа. - person elixenide; 25.02.2014
comment
Эд, есть набор чисел, которые допускают представление с плавающей запятой без какой бы то ни было потери точности. В Lua, например, есть только числа типа double и нет целых чисел, потому что нет ошибки, представляющей целые числа ниже определенного предела как числа типа double. @false оспаривает универсальность всегда несовершенного. - person Daniel Lyons; 25.02.2014
comment
Я понимаю, что некоторые числа могут быть представлены с плавающей запятой без потери точности. Но арифметика с плавающей запятой - то есть работа с двумя или более числами - неточна по своей природе. - person elixenide; 25.02.2014
comment
@EdCottrell: сложение / вычитание / умножение чисел Σ 2 ‹sup› i ‹/sup› снова даст вам такое число. Все это точно в определенных пределах. Представьте 0,875 + 0,5. А для компьютеров, работающих в двоичном формате, см. это . - person false; 25.02.2014
comment
@false Я не думаю, что мы здесь общаемся, потому что ваши комментарии не имеют отношения к моей точке зрения. См. этот вопрос и ответ, которые также имеют отношение к этому вопросу: неточность присуща стандарту IEEE 754. Тот факт, что 0.875+0.5 имеет точный правильный ответ, не обязательно означает, что компьютер получит точный ответ. Например, 0.875+0.555 также имеет точно правильный ответ, но многие языки ответят чем-то неточным, например 1.4300000000000002. - person elixenide; 25.02.2014
comment
@EdCottrell: Приведенный вами пример неуместен, потому что 0.555 не имеет формы Σ 2 ^ i. 0.555 может быть точно представлен только с основанием счисления = 10, но не с основанием счисления = 2. - person false; 26.02.2014
comment
@false Мой пример уместен, потому что вопрос не в числах вида Σ 2 ^ i. Речь идет об операции с плавающей запятой. Я написал ту арифметику с плавающей запятой, которая всегда несовершенна. Это правда. Арифметика с использованием чисел , которые не являются истинными числами с плавающей запятой, но на самом деле являются целыми степенями двойки, - это не одно и то же. Арифметика, в которой используются числа, которые должны быть представлены как числа с плавающей запятой, всегда неточна при выполнении на (двоичном) компьютере. - person elixenide; 26.02.2014
comment
@EdCottrell: Всегда включает числа, которые я упомянул. - person false; 26.02.2014
comment
@false Я бы сказал, что арифметика с плавающей запятой не включает манипуляции исключительно с числами, которые могут быть представлены точно в двоичном формате. Тем не менее, я отредактировал свой ответ, чтобы пояснить, как я использую всегда. Если бы вы проголосовали против, я был бы признателен вам за пересмотр. - person elixenide; 26.02.2014