информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
За кого нас держат?Где водятся OGRыВсе любят мед
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Линуксовый ботнет, распространяющийся... 
 Конец поддержки Internet Explorer 
 Рекордное число уязвимостей в 2021 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
Дело в том, что действительное число может быть 32, 64 и 80... 25.06.07 11:39  Число просмотров: 2738
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
<"чистая" ссылка>
> Здравствуйте, все. Такая проблема. Пишу функцию для
> перевода действительного числа в строку. Работать
> должна как можно быстрее
. Пытался осуществить
> через команды сопроцессора.

Дело в том, что действительное число может быть 32, 64 и 80 битным, которое состоит и мантиссы и экспоненты. Разрядность и того и другого не маленькая. Если придется сталкиваться с большими числами, то голупо отказываться от экспоненциальной формы. Для простой формы предложу такой алгоритм:
Заведем массивчик делителей 0.001, 0.01, 0.1, 1, 10, 100, 1000. Циклом пытаемся найти остаток от деления на каждый элемент массива, используя тот же ассемблер сопроцессора (FPREM) для нахождения каждой цифры. Не забываем вычитать из числа эту цифру, умноженную на соответствующий делитель. С отрицательностью боремся в начале. Точку ставим в нужном месте.

> Идея следующая. Загрузить число в сопроцессор командой
> fld, домножить его на что-то вроде
> 10^x, чтобы осталась только целая часть.
> После этого сохранить это число командой
> fist. Дальше всё просто.

Это лишнее.

> Оценку числа нулей в 10^x я придумал
> (3*p/10, где p – двоичный порядок
> числа). Вот только сформировать это число не получается. С
> командой fscale справиться не
> получилось.

Оценку разрядности лучше делать через логарифм.

> Может у кого пример подобного есть? Но в принципе,
> мне нужна помощь только для формирования числа
> 10^x в действительной форме
, остальное всё уже
> сделаю сам.
>
> Пишу на asm'е.
<programming>
Как преобразовать действительное число в строку? 24.06.07 16:07  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
Отредактировано 24.06.07 16:16  Количество правок: 3
<"чистая" ссылка>
Здравствуйте, все. Такая проблема. Пишу функцию для перевода действительного числа в строку. Работать должна как можно быстрее. Пытался осуществить через команды сопроцессора.

Идея следующая. Загрузить число в сопроцессор командой fld, домножить его на что-то вроде 10^x, чтобы осталась только целая часть. После этого сохранить это число командой fist. Дальше всё просто.

Оценку числа нулей в 10^x я придумал (3*p/10, где p – двоичный порядок числа). Вот только сформировать это число не получается. С командой fscale справиться не получилось.

Может у кого пример подобного есть? Но в принципе, мне нужна помощь только для формирования числа 10^x в действительной форме, остальное всё уже сделаю сам.

Пишу на asm'е.
Можно ещё 2 вопроса по поводу? 27.06.07 17:54  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
<"чистая" ссылка>
Можно ещё 2 вопроса по поводу?
1. Почему некоторые числа в сопроцессор загружаются неточно (например, 2.3 загружается как 2.2999999), и как от этого избавиться?
2. Почему, когда используешь команду fincstp без предшествующей ffree, то второе загружаемое после этого в сопроцессор не загружаются числа (говорит, что NAN - не число)? Или может быть это касяк в моём процессор (у меня Athlon 900)?
Потому что 2.3 нельзя представить в виде многочлена конечной... 29.06.07 10:14  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
Отредактировано 29.06.07 10:16  Количество правок: 2
<"чистая" ссылка>
> Можно ещё 2 вопроса по поводу?
> 1. Почему некоторые числа в сопроцессор загружаются неточно
> (например, 2.3 загружается как 2.2999999), и как от этого
> избавиться?

Потому что 2.3 нельзя представить в виде многочлена конечной длины ( x1*2^-1 + x2*2^-2 + ... + xn*2^-n ) * 2^e
Не каждое число можно представить в виде конечной десятичной дроби (1/3, например), так же не каждое десятичное дробное число можно представить в виде конечной двоичное дроби. То же число 1/3 легко представляется в троичной системе счисления как 0.1, а число 1/2 в троичной системе не представляется, хотя спокойно представляется в десятичной и двоичной.

Как с этим бороться - да просто округлять. Если 2.3 округлять до одного знака после запятой, то и получится 2.3, а если до сотого знака, то все равно получится 2.3. Бывают обратные ситуации, когда получается 2.30000000000000001.

С флоатом бывает много казусов. Например не следует использовать сравнения на "равно". Причем при отладочной печати далекие младшие разряды могут быть и не напечатаны. Если использовать флоат малой точности, то цикл от нуля до милиарда с шагом 0.001 превращается в бесконечный.

> 2. Почему, когда используешь команду
> fincstp без предшествующей
> ffree, то второе загружаемое после этого
> в сопроцессор не загружаются числа (говорит, что
> NAN - не число)? Или может быть это
> касяк в моём процессор (у меня Athlon 900)?

Это не по адресу вопрос. Надо глубоко в документацию лезть. Могу сказать, что это не косяк. Можно проверить на другом проце. В противном случае было бы много шума про такого рода баг.
Дело в том, что действительное число может быть 32, 64 и 80... 25.06.07 11:39  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
<"чистая" ссылка>
> Здравствуйте, все. Такая проблема. Пишу функцию для
> перевода действительного числа в строку. Работать
> должна как можно быстрее
. Пытался осуществить
> через команды сопроцессора.

Дело в том, что действительное число может быть 32, 64 и 80 битным, которое состоит и мантиссы и экспоненты. Разрядность и того и другого не маленькая. Если придется сталкиваться с большими числами, то голупо отказываться от экспоненциальной формы. Для простой формы предложу такой алгоритм:
Заведем массивчик делителей 0.001, 0.01, 0.1, 1, 10, 100, 1000. Циклом пытаемся найти остаток от деления на каждый элемент массива, используя тот же ассемблер сопроцессора (FPREM) для нахождения каждой цифры. Не забываем вычитать из числа эту цифру, умноженную на соответствующий делитель. С отрицательностью боремся в начале. Точку ставим в нужном месте.

> Идея следующая. Загрузить число в сопроцессор командой
> fld, домножить его на что-то вроде
> 10^x, чтобы осталась только целая часть.
> После этого сохранить это число командой
> fist. Дальше всё просто.

Это лишнее.

> Оценку числа нулей в 10^x я придумал
> (3*p/10, где p – двоичный порядок
> числа). Вот только сформировать это число не получается. С
> командой fscale справиться не
> получилось.

Оценку разрядности лучше делать через логарифм.

> Может у кого пример подобного есть? Но в принципе,
> мне нужна помощь только для формирования числа
> 10^x в действительной форме
, остальное всё уже
> сделаю сам.
>
> Пишу на asm'е.
Формат действительных чисел я уже во сне видеть буду... С... 25.06.07 14:55  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
<"чистая" ссылка>
Формат действительных чисел я уже во сне видеть буду... С массивчеком делителей напряженка будет: даже в double-формате диапазон +-10^308. Это получается порядка 600 таких делителей. Работать конечно быстро будет, но хотелось бы покороче :)
Можно без массивчика: Сначала вычисляем десятичный логарифм,... 25.06.07 18:42  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
<"чистая" ссылка>
> Формат действительных чисел я уже во сне видеть буду... С
> массивчеком делителей напряженка будет: даже в
> double-формате диапазон +-10^308. Это получается порядка
> 600 таких делителей. Работать конечно быстро будет, но
> хотелось бы покороче :)
Можно без массивчика: Сначала вычисляем десятичный логарифм, потом округляем его, вычитаем x, где x - количество десятичных знаков, которые надо вывести (точность, грубо говоря) вычисляем 10 в степени этот округленный логарифм, ето и будет первый делитель. Другие делители вычисляются умножением на 10. Массив не нужен.
DDP, спасибо за совет. Вот сырой вариант результата: 26.06.07 11:16  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
Отредактировано 26.06.07 11:23  Количество правок: 4
<"чистая" ссылка>
DDP, спасибо за совет. Вот сырой вариант результата:

.386
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.data
upr	dw ?

flag	db 0
p1	dd 0
ten	dq 10.0		; десятка для преобразования к целому
P	dq ?
fig	dq -100.02313e20
del	dq 0		; текущий делитель для выделения цифры
dig	dd ?		; очередная цифра
string	db ' .0000000000000000D-000', 0
pattern	db ' .0000000000000000D-000', 0

.code

; возведение в степень x^y
pow	proc	x: DWORD, y: DWORD
	mov	eax, y
	fld	qword ptr [eax]
	mov	eax, x
	fld	qword ptr [eax]
	ftst
	fstsw	ax
	sahf
	jnc	m1	; переход, если x >= 0
	inc	flag	; взведём флаг, если x < 0
	fabs		;xm1:	fxch
	fyl2x
	fst	st(1)
	fabs		;z; сравнимzс единицей
	fld1
	fcom
	fstsw	ax
	sahf
	jp	exit	; операнды не сравнимы
	jnc	m2	; еслиz< 1, то переход на m2
	jz	m3	; еслиz= 1, то переход на m3
; еслиz> 1, то приводим к формуле z = z1+z2, где z1 - целое, z2 - дробное, и z2 < 1
	xor	ecx, ecx	; счётчик вычитаний
m12:	inc	cx
	fsub	st(1), st(0)
	fcom
	fstsw	ax
	sahf
	jp	exit	; операнды не сравнимы
	jz	m12
	jnc	m2	; еслиz< 1, то переход на m2
	jmp	m12	; еслиz> 1, то переход на m12
m3:	mov	p1, 1
	jmp	$+7
m2:	mov	p1, ecx
	fxch
	f2xm1
	fadd		; компенсируем 1
	fild	p1	; показатель степени для fscale
	fld1
	fscale
	fxch
	ffree	st(0)
	fincstp
	fmul
; проверка на отрицательную степень
	cmp	flag, 1
	jnz	exit
	fld1
	fxch
	fdiv
exit:
	ret
pow	endp

double2str	proc	fg: DWORD, buf: LPBYTE
     ;---------- Загрузить шаблон числа
	invoke	lstrcpy, buf, addr pattern
     ;---------- Определить и сохранить знак числа
	mov	edx, fg
	mov	eax, fg
	mov	eax, [eax]
	and	eax, 80000000h
	jz	positive
	mov	ecx, buf
	mov	byte ptr [ecx], '-'
     ;---------- Сделать из числа положительное
	fld	qword ptr [edx]
	fabs
	fstp	qword ptr [edx]
positive:
     ;---------- Определить порядок числа
	fld1
	fld	qword ptr [edx]
	fyl2x
	fldl2t
	fdiv	st(1), st(0)
	ffree	st(0)
	fincstp
	fstcw	upr
	and	upr, 1111001111111111b
	fldcw	upr
	frndint
	fstp	P		; порядок числа
     ;---------- Сформировать первый делитель
	push	offset ten
	push	offset P
	call	pow
	fld	P
	fistp	P
     ;---------- Определить и сохранить знак порядка
	lea	eax, P
	mov	eax, [eax]
	cmp	eax, 0
	jl	negative
	mov	ecx, buf
	add	ecx, 19
	mov	byte ptr [ecx], '+'
     ;---------- Сохранить сам порядок
negative:
	lea	eax, P
	mov	eax, [eax]
	mov	bl, 10
	div	bl
	add	ah, 30h
	mov	ecx, buf
	add	ecx, 20
	mov	byte ptr [ecx+2], ah
	xor	ah, ah
	div	bl
	add	ah, 30h
	mov	byte ptr [ecx+1], ah
	add	al, 30h
	mov	byte ptr [ecx], al
     ;---------- Вытащить и сохранить цифры
	mov	ecx, buf
	add	ecx, 2
nextDig:
	fld	qword ptr [edx]
	fdiv	st(0), st(1)

	fstcw	upr
	or	upr, 0000110000000000b
	fldcw	upr
	frndint
	fist	dig

	fmul	st(0), st(1)
	fld	qword ptr [edx]
	fxch	st(1)
	fsubp	st(1), st(0)
	fstp	qword ptr [edx]

	fld	ten
	fdivp	st(1), st(0)

	mov	eax, dig
	add	eax, 30h
	mov	[ecx], al
	inc	ecx

	cmp	byte ptr [ecx], 'D'
	jne	nextDig	

	ret
double2str	endp

start:
	finit
	push	offset string
	push	offset fig
	call	double2str	
	invoke	ExitProcess, 0
end	start
									(c) Vedrus  

---
Возможно, через некоторое время оптимизированный вариант выложу. Если кто, что ещё посоветует по-поводу буду рад.
На здоровье! Визуально проверить это ниасилил, да и не к... 26.06.07 17:02  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
Отредактировано 26.06.07 17:11  Количество правок: 7
<"чистая" ссылка>
> Возможно, через некоторое время оптимизированный вариант
> выложу. Если кто, что ещё посоветует по-поводу буду рад.

На здоровье! Визуально проверить это ниасилил, да и не к чему. Сейчас поясню...
Во первых, что я влез. Я просто вспомнил про табличную оптимизацию. Если нужно 600 значений по 8 байт и прога от этого будет быстрее работать хотя бы процентов на 10, то нужно плюнуть на эти лишних пять килобайт и завести массив. Скорость будет однозначно выше, так как операции с возведением в степень или даже делением/умножением на сопроцессоре медленнее, чем обращение/адресация к элементу массива.
Причем чем больше массив, тем быстрее вычисления. Тригонометрия будет считаться быстрее если от таблицы с шагом 0.1 радиан перейти к таблице с шагом в 0.01 радиан раз в десять, а количество элементов таблицы увеличится с 16 до 160.
Еще со времен четвертого фортрана было несколько форматов вывода чисел с плавающей запятой. Обычная, экспоненциальная и компактная, то есть либо обычная, либо экспоненциальная в зависимости от самого числа (для очень больших и очень маленьких чисел экспоненциальная, а так - обычная). Обычная - удобочитаемая. Экспоненциальная - универсальная. Какая вам была нужна я до сих пор не понял, от этого тоже много чего зависит.
Так вот поясню: В зависимости от того что из условий более важное, алгоритм можно еще убыстрить. А именно, если требуется сделать все через жопу сопроцессор, то отлаживайте свой вариант, а если важна скорость преобразование и форма вывода только экспоненциальная, то предложу такое решение. Дла него опять же надо хорошо вспомнить школьный курс логарифмов.
Короче:
1) Вычисляем десятичный логарифм.
2) Находим разность между ним и 20.
3) Умножаем исходное число на 10 в степени разности (полученой во втором пункте).
4) Преобразуем это в целое длинное число, а затем его уже в цифровую форму через обычные несопроцессорные, а целочисленные операции. Это и будет то, что должно быть после точки в мантиссе.
5) Как вычислить экспоненту, сами догадаетесь. Преобразовать ее в цифровую форму тоже легко.
Всего в этом алгоритме будет пяток вычислительных медленный инструкций с плавающей запятой, несколько инструкций загрузки/выгрузки и преобразований к целым. Поразрядное получение цифр дробной части мантиссы будет не медленнее, чем несколько вычислений на сопроцессоре.
Идея этого всего в том, чтобы преобразовать исходное число в число в диапазоне 10^19<=X<10^20, умножив его на 10 в соответствующей степени, Это число легко преобразуется в целое и оно будет определять необходимые значащие цифры, а значение экспоненты, легко получается из десятичного логарифма исходного числа, приведением его к целому значению.
А как делают стандартные RTL функции? 25.06.07 11:11  
Автор: AlexD <Alexander> Статус: Member
<"чистая" ссылка>
Может, этого будет достаточно?
А можно пример такой Rtl-функции, а то в MSDN'е по названиям... 25.06.07 14:55  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
<"чистая" ссылка>
А можно пример такой Rtl-функции, а то в MSDN'е по названиям ничё похожего не нашёл.
wsprintf 25.06.07 16:21  
Автор: Neznaika <Alex> Статус: Member
<"чистая" ссылка>
Блин... Я на API-шках помешался. Про языковые функции и... 25.06.07 18:02  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
<"чистая" ссылка>
Блин... Я на API-шках помешался. Про языковые функции и забыл. Спасибо, что напомнили. Щас _ecvt ковыряю. Вроде не большая и делает как раз то, что мне надо.
Скажу по секрету - апишные так же реализованы:) 29.06.07 21:53  
Автор: AlexD <Alexander> Статус: Member
<"чистая" ссылка>
Например? 30.06.07 02:57  
Автор: Vedrus <Serokhvostov Anton> Статус: Member
<"чистая" ссылка>
Что например? Дизасм. листинг апишной функции привести:)? 05.07.07 03:34  
Автор: AlexD <Alexander> Статус: Member
<"чистая" ссылка>
1




Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2022 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach