-
Всем привет! Подскажите пожалуйста, как сравнить такие числа, если тип числа double?
DoubleEpsilon := 2.22044604925031e-16;
X := -9.30880643132136e+18;
if Abs(X) < DoubleEpsilon then X := 0.0;
Все, что меньше эпсилона вообще не воспринимается :(
-
Сравнение с NaN, NegInfinity, PosInfinity не помогает. Знаки сравнения на это число не реагируют.
-
> если тип числа double
сделать типом Extended
> DoubleEpsilon := 2.22044604925031e-16;
а если посмотреть, что получается после присваивания?
-
а вообще, сравнение таких чисел делают с помощью SameValue, CompareValue, IsZero
-
DoubleEpsilon как раз еще воспринимается, а вот X := -9.30880643132136e+18 уже нет. Для Double это EFloatingOverflow. Проблема в том, что Exception не генерится. Получается при вычислении тангенса в некоторых случаях.
-
можно не допускать, чтобы при вычислениях числа становились слишком малыми, умножая их на некоторый коэффициент
-
а если использовать сторонний математический юнит?
-
> Все, что меньше эпсилона вообще не воспринимается
А -9.30880643132136e+18 никак не меньше эпсилона.
-
Ну т.е. от слова совсем. :)
-
Германн © (02.05.18 02:31) [7] Я Abs использую.
-
1. Убедиться, правомочно ли мы сравнивает минус 16-тую степень с плюс 18-той. 2. Если нет, то можно, зная формат хранения вещественных чисел выдрать из них мантиссу и экспоненту, привести к одной степени и сравнивать уже их
-
> выдрать из них мантиссу и экспоненту
а выдерится ли из результата, оказавшийся меньше эпсилонного? и кстати, а вообще, результат вычислений может быть меньше машинного эпсилона?
-
>и кстати, а вообще, результат вычислений может быть меньше машинного эпсилона? Да. И Exception не срабатывает. Срабатывает, когда пытаешься сложить 2 числа.
-
> dmk © (01.05.18 14:11) [4] > DoubleEpsilon как раз еще воспринимается, а вот X := -9.30880643132136e+18 уже нет. > Для Double это EFloatingOverflow. Проблема в том, что Exception не генерится.
Вот это не понял. Могли бы пояснить? Double принимает диапазон от 2.23e-308 до 1.79e+308
У вас всего +18 степень. В чем беда?
> DoubleEpsilon := 2.22044604925031e-16; > X := -9.30880643132136e+18; > if Abs(X) < DoubleEpsilon then X := 0.0; > > Все, что меньше эпсилона вообще не воспринимается :(
Поясните, пожалуйста, смысл термина "не воспринимается". Я его не понимаю. Что происходит в приведённом вами коде? и напишите что вы ожидали. Так будет понятнее
-
>KSergey © (03.05.18 10:31) [13] Вы невнимательно читаете. Получающееся число меньше машинного эпсилона. И не ноль и не NaN. Оно не ошибочно. Оно есть, но при сложении или вычитании генерируется исключение.
>Double принимает диапазон от 2.23e-308 до 1.79e+308 Это если основание 1.79. В моем случае основание -9.308......, а значит и диапазон другой. Это же плавающая точка :)
-
> dmk © (03.05.18 12:21) [14]
-9.30880643132136e+18 или же всё таки -9.30880643132136e-18;
-
> dmk © (03.05.18 12:21) [14] > >KSergey © (03.05.18 10:31) [13] > Вы невнимательно читаете.
Чувак, я читаю внимательно. Тебе ответ нужен или разборки кто внимательно или не внимательно читает?
Ты написал вот что:
> dmk © (01.05.18 04:03) > DoubleEpsilon := 2.22044604925031e-16; > X := -9.30880643132136e+18;
Теперь скажи, что я читаю невнимательно? и, главное, зачем ты про это пишешь?
-
>Dimka Maslov © (03.05.18 13:09) [15] >-9.30880643132136e+18 или же всё таки >-9.30880643132136e-18;
-9.30880643132136e+18 = 0.00000000000000000930880643132136. Вроде так.
>KSergey © (03.05.18 13:16) [16] Значит не понимаете о чем речь.
X := Tan(X) E := DoubleEpsilon;
X = 0.00000000000000000930880643132136; E = 0.000000000000000222044604925031;
//1.0 + X = Exception //1.0 + E = No Exception
-
> dmk © (03.05.18 13:23) [17] > -9.30880643132136e+18 = 0.00000000000000000930880643132136. > > Вроде так.
В чего это вдруг??
-9.30880643132136e+18 = -9308806431321360000
-
> dmk © (03.05.18 13:23) [17] > X := Tan(X) > E := DoubleEpsilon; > > X = 0.00000000000000000930880643132136; > E = 0.000000000000000222044604925031; > > //1.0 + X = Exception > //1.0 + E = No Exception
Чета неправда. Или вопрос опций компилятора. Вот программа, только что скомпилировал, запустил: var X, E: Double;
var z: Double;
begin
X := 0.00000000000000000930880643132136;
E := 0.000000000000000222044604925031;
z := 1.0 + X;
writeln(z);
end. Выдаёт: 1.00000000000000E+0000 Никаких Exception нет.
-
Delphi в отладчике CPU пишет -9.30880643132136E18 В переменной -9.30880643132136e+18
В любом случае переполнение разрядной сетки.
-
У меня это происходит при округлении:
function Round(F: double): int64; asm .NOFRAME
cvtsd2si rax, F end;
-
> dmk © (03.05.18 13:42) [20] > Delphi в отладчике CPU пишет -9.30880643132136E18 > В переменной -9.30880643132136e+18
Славно. А нули-то при этом куда надо рисовать? куда я написал, ведь правда?
> В любом случае переполнение разрядной сетки.
Разрядной сетки чего? какой "разрядной сетки"? Я же уже приводил диапазон для Double, вы почему невнимательно читаете? впрочем, я почитал про "основание" в [14] уу.....
Это называется мантисса, а не основание, и это важно.
-
нда.. вот бы еще понять как приведённый код на asm связан с исходным вопросом...
-
кстати, возвращаемое значение из функции объявлено как int64 это вас не удивляет? 1E+18 не лезет в int64
-
> z := 1.0 + X; > > writeln(z); > > end. > > Выдаёт: > > 1.00000000000000E+0000
совпадает с описанием машинного эпсилона в вики. значит, гуд.
-
телепатирую:
вы вызвали стандартную функцию Round() для числа -9.30880643132136E18 И она рванула "переполнение". Ну так это понятно: ибо диапазон для int64 От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 т.е. грубо говоря -9.2E+18 до +9.2Е+18
А вы в него пытаетесь запихать -9.3E+18
Оно выходит за границы int64, отсюда и взрыв
Ведь так?
-
> -9.30880643132136e+18 = 0.00000000000000000930880643132136.
Да ну? С какой радости?
-
-
Ну да, неправильно понял из-за разницы в отладочном окне. Там просто E18 написано, а не +. В переменной +18. Учитывая, что тангенс у меня в диапазоне -2Pi..2PI - не ожидал, что число вылезет в обратную сторону после умножения на K.
Это просто масштабирование.
K := (gFactor * Aspect); //Масштаб(%) * соотношение сторон
Это просто графики функций:
//Графики repeat DX := (X * gFactor) + cX; NA := NormalizeAngle(A) * DegToRadD;
sinY := Sin(@NA) * K; cosY := Cos(@NA) * K; tanY := Tan(@NA) * K; cotY := Cot(@NA) * K;
with RT^ do begin FloatPixel(DX, cY + sinY, crLtYellow, 255, Alpha); FloatPixel(DX, cY + cosY, crLtGreen, 255, Alpha); FloatPixel(DX, cY + tanY, crLtBlue, 255, Alpha); FloatPixel(DX, cY + cotY, crLtRed, 255, Alpha); end;
X := (X + StepX); //Шаги в радианах A := (X * RadToDegD); //Угол для вычисления функции until (X > R);
И вот где cY + tanY вылезает Exception.
-
Это просто графики функций: https://yadi.sk/i/Y_w_IWJg3VF9fLПроверял как считаются. Если gFactor > 500% - происходит Exception. А мне надо до 100000%, чтобы отклонения посмотреть. Это свой расчет SinCosTanCot.
-
Извиняшки. Это я дурак. Tan(90°) не определен. Cosec(0°) тоже не определен. Потому и ошибка. Я не обрабатываю эту ситуацию в своей функции ;)
-
Как это всё связано с изначальным вопросом - вот бы понять. Впрочем, не важно.
Что же до приведённого куска кода - очевидно, что ошибок в нём нет, ошибки (если они вообще есть) - где-то в других частях кода. К тому же мы не знаем что за секретная функция FloatPixel и какие значения принимают переменные cY и tanY в момент возникновения проблемы. От нас это зачем-то скрывают, но посочувствовать я готов.
-
Ура, вот всё и разрешилось. А то "не понимаете, не понимаете" :)
-
>что за секретная функция FloatPixel Она не секретная. Просто пиксел распределяется на 4 пиксела и получается сглаженная линия. Удобно функции рисовать. Типа AntiAliasing'а.
procedure FloatPixel(X, Y: double; dColor: TColorRef; dAlpha, dOpacity: byte); var fX, fY, sX, sY: double; x0, y0, x1, y1: integer;
begin //Координата отсчетного пиксела if (X < 0.0) then begin x0 := Trunc(X - 1.0); x1 := (x0 + 1); //Пиксел справа fX := Abs(Frac(X)); //Дробная часть сдвигается в пиксел справа sX := (1.0 - fX); //Остаток в стартовом пикселе end else begin x0 := Trunc(X); x1 := (x0 + 1); //Пиксел справа sX := Frac(X); //Дробная часть сдвигается в пиксел справа fX := (1.0 - sX); //Остаток в стартовом пикселе end;
if (Y < 0.0) then begin y0 := Trunc(Y - 1.0); y1 := (y0 + 1); //Пиксел снизу fY := Abs(Frac(Y)); //Дробная часть сдвигается в пиксел снизу sY := (1.0 - fY); //Остаток в стартовом пикселе end else begin y0 := Trunc(Y); y1 := (y0 + 1); //Пиксел снизу sY := Frac(Y); //Дробная часть сдвигается в пиксел снизу fY := (1.0 - sY); //Остаток в стартовом пикселе end;
//Отсчетный пиксел if FClipRegion.PtInRegion(x0, y0) then FBlendPixel(PAddress32(x0, y0), dColor, dAlpha, Round((fX * fY) * dOpacity)); //Пиксел справа if FClipRegion.PtInRegion(x1, y0) then FBlendPixel(PAddress32(x1, y0), dColor, dAlpha, Round((sX * fY) * dOpacity)); //Пиксел снизу if FClipRegion.PtInRegion(x0, y1) then FBlendPixel(PAddress32(x0, y1), dColor, dAlpha, Round((fX * sY) * dOpacity)); //Пиксел по диагонали справа внизу if FClipRegion.PtInRegion(x1, y1) then FBlendPixel(PAddress32(x1, y1), dColor, dAlpha, Round((sX * sY) * dOpacity)); end;
БлендПиксел тоже не секретный. Смешивает содержимое буфера с цветом. Получается смешанный пиксел.
procedure BlendPixel(dA: QWord; AColor: TColorRef; Alpha, Opacity: byte); var A: word; ADif: word; WR, WG, WB: word;
begin //Прозрачность канала A := (((Alpha + 1) * Opacity) shr 8); //Для исходного пиксела нужно обратное насыщение ADif := (A xor $FF) + 1; //Коррекция на единицу для корректного деления на 256 A := (A + 1);
//Расчет компонентов пиксела наложения WR := TBgrPixel(AColor).r * A; WG := TBgrPixel(AColor).g * A; WB := TBgrPixel(AColor).b * A;
//Смешиваем цвета TBgrPixel(PDWord(dA)^).r := ((TBgrPixel(PDWord(dA)^).r * ADif + WR) shr 8); TBgrPixel(PDWord(dA)^).g := ((TBgrPixel(PDWord(dA)^).g * ADif + WG) shr 8); TBgrPixel(PDWord(dA)^).b := ((TBgrPixel(PDWord(dA)^).b * ADif + WB) shr 8); end;
Есть версия на асме:
procedure BlendPixel(dA: QWord; AColor: TColorRef; Alpha, Opacity: byte); const ShufMask: uint64 = $0100010001000100;
asm .NOFRAME
pxor mm0, mm0 //<- APixel pxor mm1, mm1 //<- AColor movq mm2, ShufMask //Формирование маски умножения
punpcklbw mm0, [dA] //Преобразуем байты в слова psrld mm0, 8 //Преобразование старших бит в младшие
movd mm5, AColor punpcklbw mm1, mm5 //Преобразуем байты в слова psrld mm1, 8 //Преобразование старших бит в младшие
movzx eax, Alpha //Вычислим прозрачность альфа-канала inc eax //Прибавим единицу, чтобы корректно разделить на 256 movzx r8d, Opacity //Компонента прозрачности mul eax, r8d //Умножаем shr ax, 8 //Делим на 256 mov dx, ax xor dx, $FF //<- ADif - величина цвета обратная альфе
inc edx //Прибавим единицу, чтобы корректно разделить на 256 movd mm3, edx pshufb mm3, mm2 //<- mm3: формируем маску разницы ADif
inc eax //Прибавим единицу, чтобы корректно разделить на 256 movd mm4, eax pshufb mm4, mm2 //<- mm2: формируем 64-битную альфа-маску
pmullw mm1, mm4 // AColor * Alpha = lpBR, lpBG, lpBB pmullw mm0, mm3 // APixel * ADif paddusw mm0, mm1 //APixel + AColor psrlw mm0, 8 //(APixel + AColor) div 256
packuswb mm0, mm0 //Транслируем слова в байты movd [dA], mm0 end;
У меня еще куча версий такой фукнции, в том числе и через вектора, но думаю сможете и сами реализовать.
-
ФлоатПикселом удобно Безьешку рисовать. Она же дробная получается.
-
Вот вам сразу бленд-сканлайн:
procedure BlendColorScLineMMX(dA: QWord; dLen: dword; AColor: TColorRef; Alpha, Opacity: byte); const ShufMask: uint64 = $0100010001000100;
asm .NOFRAME
xor r10, r10 mov r10d, dLen
movq mm2, ShufMask //Формирование маски умножения
pxor mm1, mm1 //Очистка старших битов movd mm5, AColor //Цвет наложения punpcklbw mm1, mm5 //Разложим байты в слова 32 -> 64 бита psrld mm1, 8 //Сдвиг вправо для очистки старших бит
movzx eax, Alpha //Вычислим прозрачность альфа-канала inc eax //Прибавим единицу, чтобы корректно разделить на 256 movzx r11d, Opacity //Компонента прозрачности mul eax, r11d //Умножаем shr ax, 8 //Делим на 256 mov dx, ax xor dx, $FF //<- ADif - величина цвета обратная альфе
inc edx //Прибавим единицу, чтобы корректно разделить на 256 movd mm3, edx pshufb mm3, mm2 //<- mm3: формируем маску разницы ADif
inc eax //Прибавим единицу, чтобы корректно разделить на 256 movd mm4, eax pshufb mm4, mm2 //<- mm2: формируем 64-битную альфа-маску
pmullw mm1, mm4 //AColor * Alpha = lpBR, lpBG, lpBB movq mm5, mm1 //Сохраним для повторения
@NextPixel: pxor mm0, mm0 //Очистим для формирования маски следующего пиксела punpcklbw mm0, [dA] //Читаем 32 бита //Транслируем байты в слова psrld mm0, 8 //Сдвиг вправо для очистки старших бит
movq mm1, mm5 //В mm5 сохраненный цвет наложения pmullw mm0, mm3 // APixel * ADif paddusw mm0, mm1 //APixel + AColor psrlw mm0, 8 //mm0 div 256
packuswb mm0, mm0 //Транслируем слова в байты movd [dA], mm0 //Пишем обратно
add dA, 4 dec r10 jnz @NextPixel end;
-
А это супер-секретный PtInRegion:
type PRegion = ^TRegion; TRegion = record private rX: integer; //Позиция слева rY: integer; //Позиция сверху rEX: integer; //Позиция справа rEY: integer; //Позиция снизу rW: integer; //Ширина включая первую точку rH: integer; //Высота включая первую точку end;
function TRegion.PtInRegion(x, y: integer): boolean; begin if (x >= rX) and (x <= rEX) then Result := (y >= rY) and (y <= rEY) else Result := false; end;
-
> dmk ©
а готового всего этого нет? или это практики ради?
-
>а готового всего этого нет? или это практики ради? Готового чего? Если про пиксели, то это я из своего класса подергал. Есть. Много. Почти все на асме. Работает шустро. Тут видео: https://yadi.sk/i/I_NwsFF-3UcVzKЭто 3D-шечка. Движок делаю небольшой.
-
В видео рендер в одном потоке. В многопоточном варианте раза в 3-10 быстрее. Зависит от процессора.
-
-
Супер! Когда же уже разработка будет доступна для простых смертных? :3 И надо бы-то наваять сайт проекта, хоть на Юкозе или (логичнее) на SourceForge'е...
А вот бы вы сделали быструю функцию (можно без исходников, в dll) которая могёт быстро отрисовать битмап на другом битмапе как "quad-2-quad"...
-
>которая могёт быстро отрисовать битмап на другом битмапе
Это тоже самое, что вверху, только в цикле:
//Первый байт пиксела $FF000000 - альфа накладываемого изображения procedure BlendDataScLine(sA, dA: QWord; dLen: dword; dOpacity: byte); const ShufMask: uint64 = $0100010001000100;
asm .NOFRAME xor r10, r10 mov r10d, dLen
movq mm6, ShufMask //Формирование маски умножения mov r8, dA //Адрес назначения
@NextPixel: pxor mm0, mm0 //Очистка регистров умножения pxor mm1, mm1 //Очистка регистров умножения
punpcklbw mm0, [sA] //Читаем исходный (налогаемый) пиксел punpcklbw mm1, [r8] //Читаем конечный пиксел psrld mm0, 8 //Сдвиг вправо для очистки старших бит psrld mm1, 8 //Сдвиг вправо для очистки старших бит
//Смешиваем альфу с прозрачностью ......... pextrw eax, mm0, 3 //Извлечем альфу из 4-го слова исходного пиксела sA inc eax //Прибавим единицу, чтобы корректно разделить на 256 movzx r11d, dOpacity //Компонента прозрачности mul eax, r11d //Умножаем shr ax, 8 //Делим на 256 mov dx, ax //<- ADif - величина цвета обратная альфе xor ax, $FF //Инвертируем альфу
//Формируем маски умножения inc eax //Прибавим единицу, чтобы корректно разделить на 256 movd mm2, eax pshufb mm2, mm6 //<- mm2: формируем 64-битную альфа-маску
inc edx //Прибавим единицу, чтобы корректно разделить на 256 movd mm3, edx pshufb mm3, mm6 //<- mm3: формируем маску разницы ADif
//Смешиваем пикселы pmullw mm1, mm2 //dA Pixel * Alpha = lpBR, lpBG, lpBB pmullw mm0, mm3 //sA Pixel * ADif paddusw mm0, mm1 //sAPixel + dAPixel psrlw mm0, 8 //mm0 div 256
packuswb mm0, mm0 //Транслируем слова в байты movd [r8], mm0 //Пишем обратно
add r8, 4 //dA + 4 add sA, 4 //sA + 4 dec r10 jnz @NextPixel end;
Рисует один битмап на другом с альфой и прозрачностью.
procedure TBitmap64.DrawSprite32(dSprite: TBitmap64; dX, dY: integer; dOpacity: byte); var x, y: integer; dR, IR: TRegion; fi: boolean; sA, dA: QWord; fX, fY: integer;
begin //Регион спрайта dR := Region(dX, dY, dSprite.Width, dSprite.Height);
if RegionIntersect(CR, dR, @IR) then begin //Регион полность в регионе отсечения fi := (IR.W = dSprite.Width) and (IR.H = dSprite.Height);
//Регион виден полностью if fi then begin fX := dX; fY := dY;
//Адрес начала строки спрайта sA := dSprite.PAddress32(0, 0); //Адрес начала строки вывода спрайта dA := PAddress32(fX, fY);
for y := 0 to dSprite.Height - 1 do begin //Смешиваем строки BlendDataScLine(sA, dA, dSprite.Width, dOpacity); //Следующая скан-линия спрайта Inc(sA, dSprite.ScLen); //Следующая скан-линия вывода Inc(dA, ScLen); //Строка вывода Inc(fY); end; end else //Регион виден не полностью begin fX := dX; fY := dY; x := 0; y := 0;
//Отсечение по X слева if (dX < FClipRegion.X) then begin fX := FClipRegion.X; x := (FClipRegion.X - dX); end;
//Отсечение по Y сверху if (dY < FClipRegion.Y) then begin fY := FClipRegion.Y; y := (FClipRegion.Y - dY); end;
//Адрес начала строки спрайта sA := dSprite.PAddress32(x, y); //Адрес начала строки вывода спрайта dA := PAddress32(fX, fY);
for y := IR.Y to IR.EY do begin //Смешиваем строки BlendDataScLine(sA, dA, IR.W, dOpacity); //Следующая скан-линия вывода Inc(dA, ScLen); //Следующая скан-линия вывода Inc(sA, dSprite.ScLen); //Строка вывода Inc(fY); end;//for end; end; end;
-
Вот результат работы BlendDataScLine: https://yadi.sk/i/EDIiJHfL3VNJAYАдрес пиксела вычисляется так: function TBitmap64.PAddress32(x, y: integer): QWord; begin Result := FBitmap.MemAddr + (y * FBitmap.ScanLength) + (x shl 2); end; Тут работы на пару вечеров, если вы программист :) А dll будет позже. Пока пилю. Я же не программер. Просто хобби.
-
Не, "quad-2-quad" - это полное аффинное преобразование. С: Произвольный 4-х-угольник в произвольный 4-х-угольник.
-
Можно и так. Только исходников пока нет.
|