LINUX.ORG.RU

lua, js - сравнение целых чисел учитывая что они хранятся как double


0

2

Известно, что как lua (по умолчанию) так и javascript хранят все числа как IEEE double. Известно также что результаты вычислений во floating point нельзя проверять на совпадаение, т.к. 0.1 + 0.2 != 0.3.

Вопрос - а как в этих языках реализуется целочисленная арифметика? Какие есть гарантии по поводу неё? Тест на js показывает, что (0.7 + 1.9 == 2.6) === false, но (7 + 19 == 26) === true, и даже (7.0 + 19.0 == 26.0), хотя 7 ровно также непредставимо в качестве конечной двоичной дроби как и 0.7. Ясно что оно не сравнивает числа с epsilon допуском, иначе в первом случае было бы true, значит или там хранится флаг целого числа (причём 7.0 тоже считается целым), или результат, достаточно близкий к целому (с достаточным числом нулей после запятой) считается целым. Где про это почитать чтобы не влететь в неожиданности с целыми числами вообще, а в особенности с большими целыми числами, где начнёт сказываться ограничение точности double?

★★★★★

емнип если число целиком влезает в мантиссу то все ок, это какраз в стандарте есть, потому шо 7.0 шо 7 это у тебя не дробь а целое число, а 0.7 дробь

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

побочный эффект:

for(var i=Math.pow(2,53)+10;i>Math.pow(2,53);--i) {

}

бесконечный цикл ^

Deleted
()

number in Lua is similar to Neko float. Both are by default double-precision IEEE floating point values. And both are customizable to use single precision or even fixed point arithmetics.

http://nekovm.org/lua

это может считаться ответом?

rikardoac
()
Ответ на: комментарий от Deleted

О, спасибо. Я почему-то думал что в IEEE мантисса представляет дробную часть из диапазона [0, 1), а оказывается она вполне себе обычное целое число. Получается что от -2^52+1 до 2^52-1 double можно свободно складывать, вычитать и умножать как целые числа?

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

IEEE мантисса представляет дробную часть из диапазона

это в нормальзованной, очевидно что если ее всегда нормализовывать то цифры 7 у нас не будет, потому полагаю иногда ее денормализуют. Скорее всего это зависит от реализации в ЯП.

Очевидно что если в js у нас «целые» числа станут «дробными» то множество веб-программистов хватит удар, потому видимо решено было до 2^52 их не шокировать

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

я идиот

все верно, число то представляет дрбную часть, но двоичной дроби, т.е.

7.0 => 0b1.11x2^2

а

0.7 => 0b1.01100110011*2^-1

Deleted
()

Вопрос - а как в этих языках реализуется целочисленная арифметика?

ЕМНИП intel гарантирует, что её FPU НЕ ошибаются, если числа <2⁶⁴, и если ты их не делишь не нацело. Т.е. 9/3 делить можно, и 123456789/9 тоже можно.

Ну а дробные (2.6 это неправильная дробь, и в двоичном виде бесконечная) - как карта ляжет.

Где про это почитать чтобы не влететь в неожиданности с целыми числами вообще, а в особенности с большими целыми числами, где начнёт сказываться ограничение точности double?

ты сам сказал - IEEE http://en.wikipedia.org/wiki/IEEE_floating_point

drBatty ★★
()
Ответ на: я идиот от Deleted

7.0 => 0b1.11x2^2

угу. Причём первая ВСЕГДА 1, и её потому НЕ хранят.

(кроме нуля). Получается 011001100110011001100110011001100…

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

IEEE-754 гарантирует, что если число представимо как m*2^e (где m и e целые и заранее ограничены в количестве бит), то оно должно быть представлено именно так. Если нет — то заранее оговоренное округление до представимого вида. Про колоду и положения карт там вроде не упоминается.

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

Про колоду и положения карт там вроде не упоминается.

конечно, не упоминается. Сам говоришь - округляется, но не говоришь - ЧТО будет округляться? А от этого ЧТО, зависит - СКОЛЬКО получится. 1.2+3.8 Вполне может получится и 4.(9), что очевидно НЕ равно 5. Сторона, куда округляется число зависит от первого отброшенного бита, а этот бит зависит разве что от погоды на Марсе. Но что самое обидное, 1.2 ты в нормальном виде НЕ представишь, ибо это 6/5. (1.0011001100110011001100110011001100), вероятность того, что отброшенный бит равен 1 равна ровно ½, 4.(9) получается тоже с вероятностью ½. Хоть ты 52й бит отбрасывай, хоть 5552й. Не важно.

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

1. Давай сейчас запусти в большом цикле скрипт/программу/whatever, который складывает double 1.2 и 3.8 и посмотри к чему там сходятся вероятности получить 4.(9) и 5, ок?

2. IEEE-754 одинаково ведет себя на разных платформах, и не зависит от погоды на марсе. То, что у тебя в руках не 1.2, а лишь его неточное представление, не значит, что возникают какие-то вероятности и неопределенности.

anonymous
()
Ответ на: комментарий от drBatty

Образованному человеку, разбирающемуся в теме понятно, что ты хочешь сказать, можешь не разжевывать. Но в качестве обучающего материала на форуме шаманские объяснения использовать не нужно.

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

1. Давай сейчас запусти в большом цикле скрипт/программу/whatever, который складывает double 1.2 и 3.8 и посмотри к чему там сходятся вероятности получить 4.(9) и 5, ок?

не ОК. Голову включи - результат зависит от того, КАКОЙ бит ты отбрасываешь: 45456й или 45457й? Если всегда будешь один и тот же бит отбрасывать - результат будет одинаковый.

Ну и к тому же, разумный компилятор считает 1.2+3.8 константой 5. Целой, ЧСХ. Потому в коде вообще никаких вычислений не будет.

2. IEEE-754 одинаково ведет себя на разных платформах, и не зависит от погоды на марсе. То, что у тебя в руках не 1.2, а лишь его неточное представление, не значит, что возникают какие-то вероятности и неопределенности.

стандарт ничего не говорит о том, ЧЕМУ равно 1.2, и даже как раз наоборот - говорит о том, что ему(стандарту) это неведомо, ибо 1.2 это нецелое число, и в конечную дробь его преобразовать можно только с потерей точности. Также в стандарте не написано, КАКАЯ КОНКРЕТНО будет потеря, даже не написано того, будет-ли 1.2 больше или меньше того, что получится.

IRL это значит только то, что если на i5 получилось 4.(9), то на i7 _может_ получится 5.0. Мало того, результат также зависит от опций компилятора, ибо считать дробные числа можно по разному. В 32х битной версии компилятор любит FPU, а в 64х битной - SSE2. В стандарте не сказано, чем они должны отличаться, и должны-ли. Максимальная погрешность указана, а вот абсолютное значение - увы. Даже знак ошибки неизвестен.

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

Но в качестве обучающего материала на форуме шаманские объяснения использовать не нужно.

Почему же? И кстати, IRL это очень похоже на магию с приворотами и Папюсом - если писать так программу, то она действительно работает в соответствии с тем, в каком доме Меркурий.

вот например здесь: lua, js - сравнение целых чисел учитывая что они хранятся как double (комментарий) другой(я надеюсь) ананимус советует воспользоваться методом тыка в качестве пруфа, а я ему объясняю, что если реципиент умирает в корчах, то это вовсе не обязательно из-за шаманских ритуалов.

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

не ОК

Вполне ок, как по мне. Цель — проверить детерминизм при равных условиях. Лень или страшно?

стандарт ничего не говорит о том, ЧЕМУ равно 1.2, и даже как раз наоборот - говорит о том, что ему(стандарту) это неведомо, ибо 1.2 это нецелое число, и в конечную дробь его преобразовать можно только с потерей точности. Также в стандарте не написано, КАКАЯ КОНКРЕТНО будет потеря, даже не написано того, будет-ли 1.2 больше или меньше того, что получится.

Действительно, он описывает представимость чисел в ограниченной форме. Соответственно, твой компилятор прекрасно знает, какие байты положить в память, чтобы (пусть и неточно) представить ~1.2. Это касательно конверсии char * -> double. Касательно вычислений — для каждой операции и ее операндов определен результат и погрешность. Берешь 6 и 5, делишь их друг на друга в 80-битном IEEE-754 FPU (как у интела например), и получаешь предсказуемый результат. "(Операция, операнды, флаги поведения) -> (результат, погрешность)".

А то, что от последовательности операций может плавать и по-разному копиться погрешность, это и дураку ясно. То же самое наблюдается и для целочисленной арифметики, только вокруг нее почему-то не напускают столько шаманского дыма.

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

Вполне ок, как по мне. Цель — проверить детерминизм при равных условиях. Лень или страшно?

я просто ЗНАЮ, что получится. Получится одно и то же, при всех равных. Это быдлокод называется - код, который работает только на компьютере автора-быдлокодера.

получаешь предсказуемый результат. "(Операция, операнды, флаги поведения) -> (результат, погрешность)«.

да. результат предсказуемый. 5±2¯⁵⁰. Ошибка очень малая, и нам это точно известно. Но НЕИЗВЕСТНО КАКАЯ ИМЕННО.

5- НЕ РАВНО 5.

А то, что от последовательности операций может плавать и по-разному копиться погрешность, это и дураку ясно. То же самое наблюдается и для целочисленной арифметики, только вокруг нее почему-то не напускают столько шаманского дыма.

нет. Для целочисленной арифметики нет округления. Округление == потеря информации. Это сложно понять? Когда ты информацию теряешь, получаешь в результате НЁХ. Это как деление на ноль - при умножении на ноль ты теряешь число, а деление - восстановление информации после умножения. Деление на ноль - попытка восстановить безвозвратно потерянное.

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

НЕИЗВЕСТНО КАКАЯ ИМЕННО

КОМУ КОНКРЕТНО НЕИЗВЕСТНО?

нет

Да.

Для целочисленной арифметики нет округления

Отжег. Спишу на стремительность суждений. Вот пример: 10/3*3/2 == 4 vs 10*3/3/2 == 5. Округление ли это, и влияет ли на него порядок операций — этот вопрос я оставляю на рассмотрение пытливому читателю. Для более продвинутых можно предложить примеры на границе INT_MAX.

Округление == потеря информации. Это сложно понять?

Нет, думаю не сложно. Ты разве это доказывал?

Это как деление на нол

Хватит это делать

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

КОМУ КОНКРЕТНО НЕИЗВЕСТНО?

очевидно - программисту/быдлокодеру неизвестно. Тебя ещё кто-то волнует?

Да.

ОК. До какого бита округляет мой процессор? А твой? Чему равен этот бит в числе value? 0 или 1? А после округления?

Вот пример: 10/3*3/2 == 4 vs 10*3/3/2 == 5. Округление ли это

нет. Это деление с остатком. Только остаток ты отбросил, потому и результат разный. Но никто и не обещал тебе, что порядок операций не должен ни на что влиять. Должен, и влияет. И что? Всё равно, 10/3 == 3 в целых числах на ЛЮБОМ железе. Потому-что ЦЕЛЫЕ - ВЕЗДЕ ОДИНАКОВЫЕ, а вот FP - разные. И они НЕ обязаны быть одинаковыми, даже с одной точностью. Это не я такой странный, это мир вокруг нас такой.

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

нет

Да.

Это деление с остатком

В плавающей точке ты назвал это округлением, а в целочисленной арифметике деление с остатком. И что?

Знаешь чем отличается умножение/деление целых чисел и чисел, построенных на мантиссе-экспоненте? В целых числах, когда разрядность результата больше, чем положено, то отбрасываются старшие биты. Это называется переполнение. Дробные биты отбрасываются всегда, поэтому это никак не называется. В числах мантисса-экспонента, когда разрядность результата больше, чем мантиссы, то отбрасываются младшие биты. Это называется округление. Дробных битов в мантиссе не бывает, так как результат всегда нормализован экспонентой.

Только остаток ты отбросил, потому и результат разный.

Если бы я не «отбросил остаток» (интересно, как бы это выглядело в коде), я бы получил одинаковый результат в целочисленной арифметике? Давай попробуем:

10/3 -> (3,1)
(3,1)*3 -> (9,?) // wtf?
(9,?)/2 -> ((4,1),?) // wtwtf?

Я в замешательстве. Ваши мнения, Господа!

Но никто и не обещал тебе, что порядок операций не должен ни на что влиять.

Я разве говорил что мне кто-то это обещал, но оказалось не так?

Должен, и влияет. И что?

Да ничего особенного. Так же как и в плавающей точке. Все операции определены и работают как им полагается.

на ЛЮБОМ железе

Как я уже писал выше, все зависит от разрядности вычислительного модуля. В FPU разрядность режет младшие биты результата, в CPU — старшие. Приближение к INT_MAX испортит твою стройную картину на разных архитектурах.

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

Знаешь чем отличается умножение/деление целых чисел и чисел, построенных на мантиссе-экспоненте? В целых числах, когда разрядность результата больше, чем положено, то отбрасываются старшие биты. Это называется переполнение. Дробные биты отбрасываются всегда, поэтому это никак не называется. В числах мантисса-экспонента, когда разрядность результата больше, чем мантиссы, то отбрасываются младшие биты. Это называется округление. Дробных битов в мантиссе не бывает, так как результат всегда нормализован экспонентой.

Вот, пилять, что получается, когда «кодерам» не дают схемотехнику, структуру процессора и ассемблер :(

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

В плавающей точке ты назвал это округлением, а в целочисленной арифметике деление с остатком. И что?

а то, что бит, с которого ты округляешь - неизвестен заранее. И _может_ быть разным. Производитель чипа вполне может повысить вдвое точность, сохранив лишний бит. На каком конкретно чипе будет выполнятся твой код - ты не знаешь, и знать не можешь.

С целым числом такого не бывает - оно всегда с одной точностью. ВСЕГДА.

Знаешь чем отличается умножение/деление целых чисел и чисел, построенных на мантиссе-экспоненте? В целых числах, когда разрядность результата больше, чем положено, то отбрасываются старшие биты. Это называется переполнение.

и это нештатная ситуация. Проще говоря - авария.

Дробные биты отбрасываются всегда, поэтому это никак не называется.

а это уже нормальная штатная работа. С FP всегда так, и это никого не удивляет.

В числах мантисса-экспонента, когда разрядность результата больше, чем мантиссы, то отбрасываются младшие биты. Это называется округление. Дробных битов в мантиссе не бывает, так как результат всегда нормализован экспонентой.

тут ты не совсем верно понимаешь: для того, что-бы ошибка была симметричной, считают на 1 бит(как минимум) больше. А затем ОКРУГЛЯЮТ(не путай с усечением). Если лишний бит 1, то к числу прибавляется 1, иначе не прибавляется. Если-бы этого не было, то при _каждой_ операции процессор немного минусовал (в среднем ½ младшего бита), и ошибка накапливалась. Но из-за округления, ошибка всегда составляет ±½ младшего бита, или в среднем равна нулю. Потому ошибки не накапливаются. С умножением схема сложнее, но суть та же.

Если бы я не «отбросил остаток» (интересно, как бы это выглядело в коде), я бы получил одинаковый результат в целочисленной арифметике?

да.

Остаток надо сохранить, и добавлять ПОСЛЕ умножения. Ты что, в школе не учился?

10/3 → (3,1)
3*3+1 -> 10
остаток - это та часть, которую мы НЕ СМОГЛИ поделить. Если мы хотим получить то, что было, надо помножить, и потом добавить остаток. Смотри: поделим 10 яблок между 3я детьми. Для этого СНАЧАЛА надо спрятать одно яблоко, а ПОТОМ поделить 9. Если это сделать иначе - дети будут драться и плакать.

В коде остаток в x86 получается в edx, а в C записывается так:

int r = 10 % 3;// остаток равен 1
напомню, что истинный остаток всегда положительный.

Как я уже писал выше, все зависит от разрядности вычислительного модуля.

просто не нужно выходить за рамки INT_MAX. Это несложно. Если тебе в коде это сложно - используй другой int, побольше. Или тебе 2⁶⁴-1 мало? Проблема FP в том, что у тебя такого выбора нет, даже безобидное 1.2 это бесконечная дробь. Т.е. у тебя ВСЕ числа неточные.

Приближение к INT_MAX испортит твою стройную картину на разных архитектурах.

IRL оно достаточно далеко. У меня сейчас комп 64х битный, т.ч. ты меня не испугал.

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

Остаток надо сохранить, и добавлять ПОСЛЕ умножения. Ты что, в школе не учился?
10/3 → (3,1)
3*3+1 -> 10

Представим, что в примере 10/3*6/2 и 10*6/3/2.

10/3 -> (3,1)
3*6+1 -> 19
19/2 -> (9,1)

10*6 -> 60
60/3 -> 20
20/2 -> 10

А ведь и правда, возможно что-то получается. То есть добавление остатка к последующей операции благополучно проносит его сквозь все выражение? Или это тут так совпало? Давай домножим на два:

9*2+1 -> 19
10*2 -> 20

Нет, ошибся. Видимо надо запоминать делители для восстановления остатка, но это уже рациональные числа, а мы говорим о целых.

anonymous
()
Ответ на: комментарий от drBatty

А можно еще попросить не писать заглавными буквами, вроде уже стопицот раз тема обсосана.

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