Comments 18
Самое веселье начинается в шейдерах. Например, если говорить о WebGL, то там целое число high precision integer на десктопе 32 разрядное, 4 байтное, т.е. до 4,294,967,296 , а вот на мобильных устройствах целое является 24 разрядным, т.е. до 16,777,216. И нигде про это не написано, нащупал методом граблей.
А прямо задаваемых типов нет? В C ведь придумали uint32_t и подобные и избежали неоднозначности между архитектурами.
Прямо такого нет. В шейдерах задаются int & float. Кроме того, можно вначале шейдера объявить точность таким вот образом:
precision highp int;
precision highp float;
float a;
Это означает, что для типа int & float будет использоваться максимально возможная точность. Только вот на разном железе она разная. На десктопах это int32, на мобильных устройствах int24.
https://0.30000000000000004.com
И ещё — всего две функции — плюс н минус показаны. Где умножение, деление, где сравнение, т.к. с числами с плавающей запятой разлижные правила математики не работают, например X + Y иногда не равно Y + X. Не говоря уже об Y * (B * C) и (A * B) * C.
Так-же и правило малых чисел стоит упомянуть, как например этот пример и что из него будет:
1.5 × 10^20 + 100.5
Очень часто проверка на ноль может не работать.
почти наверняка там где получилось 0.30..04 все операции будут работать одинаково
А еще сложение в числах с плавающей запятой коммутативно, т.е. X + Y всегда равно Y + X (но не ассоциативно) (ну и если не брать в расчет NaN+NaN)
Проверка на ноль в мире float/double не работает ± всегда. Первое правило - задать достаточную в рамках задачи точность eps, в пределах которой два числа считаются равными. Правда тут начинаются интересности со сложением чисел с сильно разными порядками.
В самом интересном случае можно одним exe на двух компах получить в зоне "обычных" значение идентичные результаты, а в зоне, приближающейся к краю диапазона (~10^-20 для float) разницу в порядок-другой.
Проверка на ноль в мире float/double не работает ± всегда.
Проверка на ноль работает всегда! Это компьютер. В первую очередь нужно уяснить для себя, что вы подразумеваете под этой проверкой на ноль. И когда требуется проверить именно на содержание нуля, а не бесконечно малого числа - это всегда работает.
Вторая проблема это когда вы задаете этот самый фиксированный EPS, то автоматически отсекаете возможный диапазон решений на более малый числах, когда текущая порядковая точность это позволяет. В этом и суть плавающего числа. А если вам нужен EPS, то подозреваю вам не плавающие числа нужны, а числа с фиксированной точкой.
А можно ещё пример в COBOL, если это возможно?
Как говорится, конец немного предсказуем. Кто-нибудь знает язык, который бы для вычислений с плавающей запятой не использовал сопроцессор? Или хоть одну причину чтобы использовать такой язык?
Все БД вовсе не используют плавающую запятую (возможность такая есть, обычно, но я не встречал в реальной жизни). У нас в системе приложены титанические усилия, что бы все вычисления проводились над типами с фиксированной точностью - ибо финансовые вычисления, и флоаты для них смерть. Вот и причина.
мне кажется акцент статьи не туда смотрит, куда лучше объяснять всё на примерах того, как именно операции в "голове" у языка происходят. Например если вы хотите 0.1 умножить на целое, а потом прибавить опять дробное 0.3, то сразу вылезет проблема (в некоторых языках) с тем, что после умножения у вас будет целое и после сложения останется целое, то есть фактически 0.1 вы просто теряете, поэтому в разных языках формулу придется использовать по-разному или явно указывать тип, что-то вроде (double)(0.3*x)+0.1
Дроби это маленькая часть чисел с плавающей запятой. То, что ограничивается числами типа x.yz, легко решается путем перехода данных "от рублей к копейкам". Для операций со всем остальным приходится мириться, что не только данные представляются неточно, но и результат операций тоже вычисляется неточно. Ну, и конечно, a+b+c начинает зависеть от порядка вычислений.
В C# меня бесит, что стандартный вывод плавающих чисел идёт через запятую, а стандартный парсинг - через точку. Идиотизм.
только сегодня столкнулся с "подарком" от вычислений с плавающей точкой.
Нужно было запускать расчеты толщины пластины с разной точность. Использован был numpy.
import numpy as np
np.arange(0.1, 0.5, 0.1) # array([0.1, 0.2, 0.3, 0.4])
np.arange(0.6, 1.1, 0.1) # array([0.6, 0.7, 0.8, 0.9, 1. , 1.1])
np.arange(0.6, 1.0, 0.1) # array([0.6, 0.7, 0.8, 0.9])
np.arange(1.6, 2.1, 0.1) # array([1.6, 1.7, 1.8, 1.9, 2. ])
np.arange(0.6, 1.2, 0.1) # array([0.6, 0.7, 0.8, 0.9, 1. , 1.1])
Строка 2 нормальное логичное поведение из парадигмы питона, не учитывать последний элемента
Строка 3, ....
Строка 4,кажется что разработчики ненавидят цифру 1
Строка 5, почему не совпадает с 3!?
Строка 6, почему совпадает с 3!?
Мне кажется 3 и 4й примеры работы прекрасны. Разработчики numpy похожу что в курсе и в документации об этом как бы сказано(мне кажется это повод удалить саму функцию или запретить использование для float, а не писать что поведение ну типа странное).
Да, понимаю что float не так прост, но почему библиотека которая позиционирует себя как что-то для работы с точными вычислениями, такое выдает!? И вы еще боритесь за написание кода высокой культуры? Сколько скрытых ошибок благодаря такому поведению кода можно ожидать?
Вычисления с плавающей запятой: сравниваем вывод в разных языках